diff --git a/test/domain/weather/get_favorite_cities_stream_use_case_impl_test.dart b/test/domain/weather/get_favorite_cities_stream_use_case_impl_test.dart new file mode 100644 index 00000000..b43eb5dc --- /dev/null +++ b/test/domain/weather/get_favorite_cities_stream_use_case_impl_test.dart @@ -0,0 +1,58 @@ +import 'package:flutter_template/domain/weather/get_favorite_cities_stream_use_case.dart'; +import 'package:flutter_template/domain/weather/get_favorite_cities_stream_use_case_impl.dart'; +import 'package:flutter_template/foundation/unit.dart'; +import 'package:flutter_template/repository/weather/weather_repository.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../exception/test_exceptions.dart'; +import '../../extensions/stream_extensions.dart'; +import '../../mocks/mocks.dart'; +import '../../test_models/city_models.dart'; + +void main() { + late WeatherRepository weatherRepository; + late GetFavoriteCitiesStreamUseCase getFavoriteCitiesStreamUseCase; + + setUp(() { + weatherRepository = MockWeatherRepository(); + getFavoriteCitiesStreamUseCase = GetFavoriteCitiesStreamUseCaseImpl( + weatherRepository: weatherRepository); + }); + + tearDown(() { + resetMocktailState(); + }); + + test( + "Given get favorite cities stream use case called, When no error occurs, Then correct data is emitted", + () { + // Given + final testCityList = singleCityList; + when(() => weatherRepository.getFavoriteCitiesStream()) + .thenAnswer((invocation) => Stream.value(testCityList)); + + // When + expect( + getFavoriteCitiesStreamUseCase(unit), + // Then + emitsInOrder([testCityList]), + ); + }, + ); + + test( + "Given get favorite cities stream use case called, When error occurs, Then no data is emitted", + () { + // Given + final testException = TestException(); + when(() => weatherRepository.getFavoriteCitiesStream()) + .thenAnswer((invocation) => Stream.error(testException)); + + // When + getFavoriteCitiesStreamUseCase(unit) + // Then + .emitsNothing; + }, + ); +} diff --git a/test/domain/weather/set_city_favorite_use_case_impl_test.dart b/test/domain/weather/set_city_favorite_use_case_impl_test.dart new file mode 100644 index 00000000..583f585b --- /dev/null +++ b/test/domain/weather/set_city_favorite_use_case_impl_test.dart @@ -0,0 +1,61 @@ +import 'package:flutter_template/domain/entity/base/result/result.dart'; +import 'package:flutter_template/domain/weather/set_city_favorite_use_case_impl.dart'; +import 'package:flutter_template/repository/weather/weather_repository.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../exception/test_exceptions.dart'; +import '../../extensions/mock_extensions.dart'; +import '../../mocks/mocks.dart'; +import '../../test_models/city_models.dart'; + +void main() { + late WeatherRepository weatherRepository; + late SetCityFavoriteUseCaseImpl setCityFavoriteUseCase; + + setUp(() { + weatherRepository = MockWeatherRepository(); + setCityFavoriteUseCase = SetCityFavoriteUseCaseImpl( + weatherRepository: weatherRepository, + ); + }); + + tearDown(() { + resetMocktailState(); + }); + + test( + "Given set city favorite use case is called, When no error occurs, Then Result.success is returned", + () async { + // Given + final testCity = city1; + when(() => weatherRepository.setCityAsFavorite(testCity)).justRun(); + + // When + final result = await setCityFavoriteUseCase(param: testCity); + + // Then + expect(result, isA()); + verify(() => weatherRepository.setCityAsFavorite(testCity)).called(1); + }, + ); + + test( + "Given set city favorite use case is called, When error occurs, Then Result.failure is returned", + () async { + // Given + final testCity = city1; + final testException = TestException(); + when(() => weatherRepository.setCityAsFavorite(testCity)) + .thenThrow(testException); + + // When + final result = await setCityFavoriteUseCase(param: testCity); + + // Then + expect(result, isA()); + expect((result as Error).exception, same(testException)); + verify(() => weatherRepository.setCityAsFavorite(testCity)).called(1); + }, + ); +} diff --git a/test/extensions/stream_extensions.dart b/test/extensions/stream_extensions.dart index aca124b7..f69d5aa4 100644 --- a/test/extensions/stream_extensions.dart +++ b/test/extensions/stream_extensions.dart @@ -4,4 +4,6 @@ extension StreamEmitsInOrderExt on Stream { void inOrder(Iterable matchers) { expect(this, emitsInOrder(matchers)); } + + get emitsNothing => expect(this, neverEmits(isA())); } \ No newline at end of file diff --git a/test/mocks/mocks.dart b/test/mocks/mocks.dart index 10b174aa..33291600 100644 --- a/test/mocks/mocks.dart +++ b/test/mocks/mocks.dart @@ -7,6 +7,7 @@ import 'package:flutter_template/repository/weather/domain_weather_mapper.dart'; import 'package:flutter_template/repository/weather/local_city_mapper.dart'; import 'package:flutter_template/repository/weather/local_day_weather_mapper.dart'; import 'package:flutter_template/repository/weather/local_weather_mapper.dart'; +import 'package:flutter_template/repository/weather/weather_repository.dart'; import 'package:flutter_template/services/weather/local/weather_local_service.dart'; import 'package:flutter_template/services/weather/remote/weather_remote_service.dart'; import 'package:mocktail/mocktail.dart'; @@ -30,6 +31,8 @@ class MockLocalDayWeatherMapper extends Mock implements LocalDayWeatherMapper {} // Repositories class MockDateRepository extends Mock implements DateRepository {} +class MockWeatherRepository extends Mock implements WeatherRepository {} + // Interactors class MockFavoriteWeatherInteractor extends Mock implements FavoriteWeatherInteractor {} diff --git a/test/presentation/unit/destinations/weather/search/search_view_model_test.dart b/test/presentation/unit/destinations/weather/search/search_view_model_test.dart index e50f3d72..b29f3d17 100644 --- a/test/presentation/unit/destinations/weather/search/search_view_model_test.dart +++ b/test/presentation/unit/destinations/weather/search/search_view_model_test.dart @@ -18,7 +18,7 @@ import '../../../../../extensions/mock_extensions.dart'; import '../../../../../extensions/stream_extensions.dart'; import '../../../../../mocks/mocks.dart'; import '../../../../base/test_helpers.dart'; -import '../models/ui_city_models.dart'; +import '../../../../../test_models/ui_city_models.dart'; void main() { late SearchNavigator searchNavigator; diff --git a/test/repository/weather/domain_city_mapper_impl_test.dart b/test/repository/weather/domain_city_mapper_impl_test.dart new file mode 100644 index 00000000..1b7045a0 --- /dev/null +++ b/test/repository/weather/domain_city_mapper_impl_test.dart @@ -0,0 +1,101 @@ +import 'package:collection/collection.dart'; +import 'package:flutter_template/domain/entity/weather/city.dart'; +import 'package:flutter_template/repository/weather/domain_city_mapper.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../test_models/city_models.dart'; +import '../../test_models/local_city_data_models.dart'; +import '../../test_models/remote_city_models.dart'; + +void main() { + late DomainCityMapper domainCityMapper; + + setUp(() { + domainCityMapper = DomainCityMapperImpl(); + }); + + tearDown(() { + resetMocktailState(); + }); + + test( + "Given local city data, When map called, Then city is returned", + () { + // Given + final localCityData = localCityData1; + final city = city1; + + // When + final result = domainCityMapper.map(localCityData); + + // Then + expect(result, isA()); + expect(result.title, city.title); + expect(result.location, city.location); + expect(result.id, city.id); + expect(result.locationType, city.locationType); + }, + ); + + test( + "Given local city data list, When map list called, Then city list is returned", + () { + // Given + final localCityData = allLocalCityDataList; + final cityList = allCityList; + + // When + final result = domainCityMapper.mapList(localCityData); + + // Then + expect(result, isA>()); + result.forEachIndexed((index, city) { + expect(city.title, cityList[index].title); + expect(city.location, cityList[index].location); + expect(city.id, cityList[index].id); + expect(city.locationType, cityList[index].locationType); + }); + }, + ); + + test( + "Given remote city, When map remote city called, Then city is returned", + () { + // Given + const remoteCityData = remoteCity1; + final city = city1; + + // When + final result = domainCityMapper.mapRemoteCity(remoteCityData); + + // Then + expect(result, isA()); + expect(result.title, city.title); + expect(result.location, city.location); + expect(result.id, city.id); + expect(result.locationType, city.locationType); + }, + ); + + test( + "Given local city data list, When map list called, Then city list is returned", + () { + // Given + const remoteCityList = allRemoteCityList; + final cityList = allCityList; + + // When + final result = domainCityMapper.mapRemoteCityList(remoteCityList); + + // Then + expect(result, isA>()); + result.forEachIndexed((index, city) { + expect(city.title, cityList[index].title); + expect(city.location, cityList[index].location); + expect(city.id, cityList[index].id); + expect(city.locationType, cityList[index].locationType); + }); + }, + ); +} diff --git a/test/repository/weather/models/city_models.dart b/test/repository/weather/models/city_models.dart deleted file mode 100644 index efaeb6a3..00000000 --- a/test/repository/weather/models/city_models.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter_template/domain/entity/weather/city.dart'; - -final cityList = [ - City( - id: 1, - title: "title 1", - locationType: "locationType 1", - location: "location 1", - ), -]; diff --git a/test/repository/weather/models/local_city_data_models.dart b/test/repository/weather/models/local_city_data_models.dart deleted file mode 100644 index 2c8d511a..00000000 --- a/test/repository/weather/models/local_city_data_models.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter_template/services/base/database/app_database.dart'; - -final localCityDataList = [ - LocalCityData( - woeid: 1, - title: "title 1", - locationType: "locationType 1", - location: "location 1", - ), -]; diff --git a/test/repository/weather/weather_repository_impl_test.dart b/test/repository/weather/weather_repository_impl_test.dart index 75e85174..a008a41c 100644 --- a/test/repository/weather/weather_repository_impl_test.dart +++ b/test/repository/weather/weather_repository_impl_test.dart @@ -14,14 +14,14 @@ import 'package:mocktail/mocktail.dart'; import '../../exception/test_exceptions.dart'; import '../../mocks/mocks.dart'; -import 'models/city_models.dart'; -import 'models/local_city_data_models.dart'; -import 'models/local_city_with_weather_models.dart'; -import 'models/local_day_weather_companion.dart'; -import 'models/local_day_weather_models.dart'; -import 'models/local_weather_companion_models.dart'; -import 'models/remote_weather_models.dart'; -import 'models/weather_models.dart'; +import '../../test_models/city_models.dart'; +import '../../test_models/local_city_data_models.dart'; +import '../../test_models/local_city_with_weather_models.dart'; +import '../../test_models/local_day_weather_companion.dart'; +import '../../test_models/local_day_weather_models.dart'; +import '../../test_models/local_weather_companion_models.dart'; +import '../../test_models/remote_weather_models.dart'; +import '../../test_models/weather_models.dart'; void main() { late WeatherLocalService weatherLocalService; @@ -65,8 +65,8 @@ void main() { "Given local service returns list of LocalCityData, When getFavoriteCitiesList is called, Then Future> is returned", () async { // Given - final localCityData = localCityDataList; - final cityData = cityList; + final localCityData = singleLocalCityDataList; + final cityData = singleCityList; when(() => weatherLocalService.getFavouriteCities()) .thenAnswer((_) => Future.value(localCityData)); when(() => domainCityMapper.mapList(localCityData)).thenReturn(cityData); @@ -104,8 +104,8 @@ void main() { "Given local services returns stream of local city data, When getFavoriteCitiesStream is called, then Stream> is returned", () { // Given - final localCityData = localCityDataList; - final cityData = cityList; + final localCityData = singleLocalCityDataList; + final cityData = singleCityList; when(() => weatherLocalService.getFavoriteCitiesStream()) .thenAnswer((_) => Stream.value(localCityData)); when(() => domainCityMapper.mapList(localCityData)).thenReturn(cityData); @@ -218,7 +218,7 @@ void main() { () async { // Given const date = Date(year: 1970, month: 1, day: 1); - final localCityData = localCityDataList; + final localCityData = singleLocalCityDataList; final localCity = localCityData.first; final remoteWeatherData = remoteWeather; const localWeatherCompanionData = localWeatherCompanion; @@ -271,7 +271,7 @@ void main() { // Given const date = Date(year: 1970, month: 1, day: 1); const nowDate = Date(year: 2021, month: 1, day: 31); - final localCityData = localCityDataList; + final localCityData = singleLocalCityDataList; final localCity = localCityData.first; final remoteWeatherData = remoteWeather; const localWeatherCompanionData = localWeatherCompanion; @@ -324,7 +324,7 @@ void main() { () async { // Given const date = Date(year: 1970, month: 1, day: 1); - final localCityData = localCityDataList; + final localCityData = singleLocalCityDataList; final localCity = localCityData.first; const localWeatherCompanionData = localWeatherCompanion; final localDayWeatherCompanionData = localDayWeatherCompanionList; diff --git a/test/test_models/city_models.dart b/test/test_models/city_models.dart new file mode 100644 index 00000000..11677039 --- /dev/null +++ b/test/test_models/city_models.dart @@ -0,0 +1,24 @@ +import 'package:flutter_template/domain/entity/weather/city.dart'; + +final singleCityList = [ + city1, +]; + +final allCityList = [ + city1, + city2, +]; + +final city1 = City( + id: 1, + title: "title 1", + locationType: "locationType 1", + location: "location 1", +); + +final city2 = City( + id: 2, + title: "title 2", + locationType: "locationType 2", + location: "location 2", +); diff --git a/test/test_models/local_city_data_models.dart b/test/test_models/local_city_data_models.dart new file mode 100644 index 00000000..18f74262 --- /dev/null +++ b/test/test_models/local_city_data_models.dart @@ -0,0 +1,24 @@ +import 'package:flutter_template/services/base/database/app_database.dart'; + +final singleLocalCityDataList = [ + localCityData1, +]; + +final allLocalCityDataList = [ + localCityData1, + localCityData2, +]; + +final localCityData1 = LocalCityData( + woeid: 1, + title: "title 1", + locationType: "locationType 1", + location: "location 1", +); + +final localCityData2 = LocalCityData( + woeid: 2, + title: "title 2", + locationType: "locationType 2", + location: "location 2", +); diff --git a/test/repository/weather/models/local_city_with_weather_models.dart b/test/test_models/local_city_with_weather_models.dart similarity index 100% rename from test/repository/weather/models/local_city_with_weather_models.dart rename to test/test_models/local_city_with_weather_models.dart diff --git a/test/repository/weather/models/local_day_weather_companion.dart b/test/test_models/local_day_weather_companion.dart similarity index 100% rename from test/repository/weather/models/local_day_weather_companion.dart rename to test/test_models/local_day_weather_companion.dart diff --git a/test/repository/weather/models/local_day_weather_models.dart b/test/test_models/local_day_weather_models.dart similarity index 100% rename from test/repository/weather/models/local_day_weather_models.dart rename to test/test_models/local_day_weather_models.dart diff --git a/test/repository/weather/models/local_weather_companion_models.dart b/test/test_models/local_weather_companion_models.dart similarity index 100% rename from test/repository/weather/models/local_weather_companion_models.dart rename to test/test_models/local_weather_companion_models.dart diff --git a/test/test_models/remote_city_models.dart b/test/test_models/remote_city_models.dart new file mode 100644 index 00000000..6494c5b3 --- /dev/null +++ b/test/test_models/remote_city_models.dart @@ -0,0 +1,20 @@ +import 'package:flutter_template/services/entity/weather/remote/remote_city.dart'; + +const allRemoteCityList = [ + remoteCity1, + remoteCity2, +]; + +const remoteCity1 = RemoteCity( + woeid: 1, + title: "title 1", + locationType: "locationType 1", + location: "location 1", +); + +const remoteCity2 = RemoteCity( + woeid: 2, + title: "title 2", + locationType: "locationType 2", + location: "location 2", +); diff --git a/test/repository/weather/models/remote_weather_models.dart b/test/test_models/remote_weather_models.dart similarity index 100% rename from test/repository/weather/models/remote_weather_models.dart rename to test/test_models/remote_weather_models.dart diff --git a/test/presentation/unit/destinations/weather/models/ui_city_models.dart b/test/test_models/ui_city_models.dart similarity index 100% rename from test/presentation/unit/destinations/weather/models/ui_city_models.dart rename to test/test_models/ui_city_models.dart diff --git a/test/repository/weather/models/weather_models.dart b/test/test_models/weather_models.dart similarity index 100% rename from test/repository/weather/models/weather_models.dart rename to test/test_models/weather_models.dart