From c2098ec3340effc0df18b44b78dddb26dc6920bc Mon Sep 17 00:00:00 2001 From: Iiro Krankka Date: Sat, 15 Sep 2018 16:30:18 +0300 Subject: [PATCH 1/3] Upgrade mockito dependency and update tests to new mockito version. --- .../showtimes/showtime_page_view_model.dart | 4 ++- pubspec.yaml | 2 +- test/redux/actor/actor_middleware_test.dart | 8 ++--- test/redux/event/event_middleware_test.dart | 22 +++++++------- test/redux/show/show_middleware_test.dart | 30 +++++++++---------- .../theater/theater_middleware_test.dart | 6 ++-- test/test_utils.dart | 6 ++-- test/ui/events/events_page_test.dart | 4 +-- .../showtime_date_selector_test.dart | 13 ++++---- test/ui/theater_list/theater_list_test.dart | 12 ++++---- 10 files changed, 53 insertions(+), 54 deletions(-) diff --git a/lib/ui/showtimes/showtime_page_view_model.dart b/lib/ui/showtimes/showtime_page_view_model.dart index 9d11da59..47a384bc 100644 --- a/lib/ui/showtimes/showtime_page_view_model.dart +++ b/lib/ui/showtimes/showtime_page_view_model.dart @@ -7,6 +7,8 @@ import 'package:inkino/redux/show/show_selectors.dart'; import 'package:meta/meta.dart'; import 'package:redux/redux.dart'; +typedef void DateChangeCallback(DateTime newDate); + class ShowtimesPageViewModel { ShowtimesPageViewModel({ @required this.status, @@ -21,7 +23,7 @@ class ShowtimesPageViewModel { final List dates; final DateTime selectedDate; final List shows; - final Function(DateTime) changeCurrentDate; + final DateChangeCallback changeCurrentDate; final Function refreshShowtimes; static ShowtimesPageViewModel fromStore(Store store) { diff --git a/pubspec.yaml b/pubspec.yaml index 189b0f09..23e4cda8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - mockito: ^2.2.3 + mockito: ^3.0.0 flutter: uses-material-design: true diff --git a/test/redux/actor/actor_middleware_test.dart b/test/redux/actor/actor_middleware_test.dart index 44740085..4bc901ca 100644 --- a/test/redux/actor/actor_middleware_test.dart +++ b/test/redux/actor/actor_middleware_test.dart @@ -48,12 +48,12 @@ void main() { }); test('FetchActorAvatarsAction - successful API request', () async { - when(mockTMDBApi.findAvatarsForActors(typed(any), typed(any))) + when(mockTMDBApi.findAvatarsForActors(any, any)) .thenAnswer((_) => Future.value(actorsWithAvatars)); await middleware.call(null, FetchActorAvatarsAction(event), next); expect(actionLog.length, 4); - expect(actionLog[0], const isInstanceOf()); + expect(actionLog[0], const TypeMatcher()); ActorsUpdatedAction actorsUpdated = actionLog[1]; expect(actorsUpdated.actors, event.actors); @@ -67,8 +67,8 @@ void main() { }); test('FetchActorAvatarsAction - handles errors silently', () async { - when(mockTMDBApi.findAvatarsForActors(typed(any), typed(any))) - .thenAnswer((_) => Future.value(Error())); + when(mockTMDBApi.findAvatarsForActors(any, any)) + .thenAnswer((_) => Future.error(Error())); await middleware.call(null, FetchActorAvatarsAction(event), next); }); diff --git a/test/redux/event/event_middleware_test.dart b/test/redux/event/event_middleware_test.dart index abd4b781..539c5c6c 100644 --- a/test/redux/event/event_middleware_test.dart +++ b/test/redux/event/event_middleware_test.dart @@ -43,7 +43,7 @@ void main() { test( 'when called with InitCompleteAction, should dispatch a ReceivedEventsAction with now playing and upcoming events', () async { - when(mockFinnkinoApi.getNowInTheatersEvents(typed(any))) + when(mockFinnkinoApi.getNowInTheatersEvents(any)) .thenAnswer((_) => Future.value(nowInTheatersEvents)); when(mockFinnkinoApi.getUpcomingEvents()) .thenAnswer((_) => Future.value(upcomingEvents)); @@ -52,8 +52,8 @@ void main() { null, InitCompleteAction(null, theater), next); expect(actionLog.length, 3); - expect(actionLog[0], const isInstanceOf()); - expect(actionLog[1], const isInstanceOf()); + expect(actionLog[0], const TypeMatcher()); + expect(actionLog[1], const TypeMatcher()); final ReceivedEventsAction action = actionLog[2]; expect(action.nowInTheatersEvents, nowInTheatersEvents); @@ -64,7 +64,7 @@ void main() { test( 'when called with ChangeCurrentTheaterAction, should request events for the theater', () async { - when(mockFinnkinoApi.getNowInTheatersEvents(typed(any))) + when(mockFinnkinoApi.getNowInTheatersEvents(any)) .thenAnswer((_) => Future.value(nowInTheatersEvents)); when(mockFinnkinoApi.getUpcomingEvents()) .thenAnswer((_) => Future.value(upcomingEvents)); @@ -81,7 +81,7 @@ void main() { ); Theater captured = - verify(mockFinnkinoApi.getNowInTheatersEvents(typed(captureAny))) + verify(mockFinnkinoApi.getNowInTheatersEvents(captureAny)) .captured .first; expect(captured.id, 'changed'); @@ -92,18 +92,18 @@ void main() { test( 'when InitCompleteAction results in an error, should dispatch an ErrorLoadingEventsAction', () async { - when(mockFinnkinoApi.getNowInTheatersEvents(typed(any))) - .thenAnswer((_) => Future.value(Error())); + when(mockFinnkinoApi.getNowInTheatersEvents(any)) + .thenAnswer((_) => Future.error(Error())); when(mockFinnkinoApi.getUpcomingEvents()) - .thenAnswer((_) => Future.value(Error())); + .thenAnswer((_) => Future.error(Error())); await middleware.call( null, InitCompleteAction(null, theater), next); expect(actionLog.length, 3); - expect(actionLog[0], const isInstanceOf()); - expect(actionLog[1], const isInstanceOf()); - expect(actionLog[2], const isInstanceOf()); + expect(actionLog[0], const TypeMatcher()); + expect(actionLog[1], const TypeMatcher()); + expect(actionLog[2], const TypeMatcher()); }, ); }); diff --git a/test/redux/show/show_middleware_test.dart b/test/redux/show/show_middleware_test.dart index d058d7a5..fca978cd 100644 --- a/test/redux/show/show_middleware_test.dart +++ b/test/redux/show/show_middleware_test.dart @@ -53,7 +53,7 @@ void main() { // passed. As DateTime(2018) will mean the very first hour and minute // in January, all the show times in test assets will be after this date. Clock.getCurrentTime = () => startOf2018; - when(mockFinnkinoApi.getSchedule(theater, typed(any))) + when(mockFinnkinoApi.getSchedule(theater, any)) .thenAnswer((_) => Future.value([ Show(start: DateTime(2018, 02, 21)), Show(start: DateTime(2018, 02, 21)), @@ -68,9 +68,9 @@ void main() { verify(mockFinnkinoApi.getSchedule(theater, null)); expect(actionLog.length, 4); - expect(actionLog[0], const isInstanceOf()); - expect(actionLog[1], const isInstanceOf()); - expect(actionLog[2], const isInstanceOf()); + expect(actionLog[0], const TypeMatcher()); + expect(actionLog[1], const TypeMatcher()); + expect(actionLog[2], const TypeMatcher()); final ReceivedShowsAction receivedShowsAction = actionLog[3]; expect(receivedShowsAction.shows.length, 3); @@ -82,7 +82,7 @@ void main() { () async { // Given Clock.getCurrentTime = () => DateTime(2018, 3); - when(mockFinnkinoApi.getSchedule(theater, typed(any))) + when(mockFinnkinoApi.getSchedule(theater, any)) .thenAnswer((_) => Future.value( [ Show(start: DateTime(2018, 02, 21)), @@ -99,8 +99,8 @@ void main() { verify(mockFinnkinoApi.getSchedule(theater, startOf2018)); expect(actionLog.length, 3); - expect(actionLog[0], const isInstanceOf()); - expect(actionLog[1], const isInstanceOf()); + expect(actionLog[0], const TypeMatcher()); + expect(actionLog[1], const TypeMatcher()); final ReceivedShowsAction receivedShowsAction = actionLog[2]; expect(receivedShowsAction.shows.length, 1); @@ -111,8 +111,8 @@ void main() { 'when InitCompleteAction results in an error, should dispatch an ErrorLoadingShowsAction', () async { // Given - when(mockFinnkinoApi.getSchedule(typed(any), typed(any))) - .thenAnswer((_) => Future.value(Error())); + when(mockFinnkinoApi.getSchedule(any, any)) + .thenAnswer((_) => Future.error(Error())); // When await middleware.call( @@ -120,10 +120,10 @@ void main() { // Then expect(actionLog.length, 4); - expect(actionLog[0], const isInstanceOf()); - expect(actionLog[1], const isInstanceOf()); - expect(actionLog[2], const isInstanceOf()); - expect(actionLog[3], const isInstanceOf()); + expect(actionLog[0], const TypeMatcher()); + expect(actionLog[1], const TypeMatcher()); + expect(actionLog[2], const TypeMatcher()); + expect(actionLog[3], const TypeMatcher()); }, ); @@ -135,8 +135,8 @@ void main() { await middleware.call(mockStore, UpdateShowDatesAction(), next); expect(actionLog.length, 2); - expect(actionLog[0], const isInstanceOf()); - expect(actionLog[1], const isInstanceOf()); + expect(actionLog[0], const TypeMatcher()); + expect(actionLog[1], const TypeMatcher()); ShowDatesUpdatedAction action = actionLog[1]; expect( diff --git a/test/redux/theater/theater_middleware_test.dart b/test/redux/theater/theater_middleware_test.dart index 9c559180..23ae860d 100644 --- a/test/redux/theater/theater_middleware_test.dart +++ b/test/redux/theater/theater_middleware_test.dart @@ -30,7 +30,7 @@ void main() { group('called with InitAction', () { test('loads the preloaded theaters', () async { // Given - when(mockAssetBundle.loadString(typed(any))) + when(mockAssetBundle.loadString(any)) .thenAnswer((_) => theatersXml()); // When @@ -43,7 +43,7 @@ void main() { test('when a persisted theater id exists, uses that as a default', () async { - when(mockAssetBundle.loadString(typed(any))) + when(mockAssetBundle.loadString(any)) .thenAnswer((_) => theatersXml()); when(mockPreferences.getString(TheaterMiddleware.kDefaultTheaterId)) .thenReturn('001'); @@ -59,7 +59,7 @@ void main() { test( 'when no persisted theater id, uses the first theater as a default', () async { - when(mockAssetBundle.loadString(typed(any))) + when(mockAssetBundle.loadString(any)) .thenAnswer((_) => theatersXml()); when(mockPreferences.getString(TheaterMiddleware.kDefaultTheaterId)) .thenReturn(null); diff --git a/test/test_utils.dart b/test/test_utils.dart index dfc4b834..df4579a8 100644 --- a/test/test_utils.dart +++ b/test/test_utils.dart @@ -31,12 +31,12 @@ MockHttpClient createMockImageHttpClient(io.SecurityContext _) { final MockHttpClientResponse response = MockHttpClientResponse(); final MockHttpHeaders headers = MockHttpHeaders(); - when(client.getUrl(typed(any))).thenAnswer((_) => Future.value(request)); + when(client.getUrl(any)).thenAnswer((_) => Future.value(request)); when(request.headers).thenReturn(headers); when(request.close()).thenAnswer((_) => Future.value(response)); when(response.contentLength).thenReturn(_transparentImage.length); - when(response.statusCode).thenReturn(io.HttpStatus.OK); - when(response.listen(typed(any))).thenAnswer((Invocation invocation) { + when(response.statusCode).thenReturn(io.HttpStatus.ok); + when(response.listen(any)).thenAnswer((Invocation invocation) { final void Function(List) onData = invocation.positionalArguments[0]; final void Function() onDone = invocation.namedArguments[#onDone]; final void Function(Object, [StackTrace]) onError = invocation.namedArguments[#onError]; diff --git a/test/ui/events/events_page_test.dart b/test/ui/events/events_page_test.dart index 4e0c4337..e10e53dc 100644 --- a/test/ui/events/events_page_test.dart +++ b/test/ui/events/events_page_test.dart @@ -93,12 +93,12 @@ void main() { // Building the events page should trigger the navigator observer // once. - verify(observer.didPush(typed(any), typed(any))); + verify(observer.didPush(any, any)); await tester.tap(find.text('Test Title')); await tester.pumpAndSettle(); - verify(observer.didPush(typed(any), typed(any))); + verify(observer.didPush(any, any)); expect(find.byType(EventDetailsPage), findsOneWidget); }, ); diff --git a/test/ui/showtimes/showtime_date_selector_test.dart b/test/ui/showtimes/showtime_date_selector_test.dart index 8ab04fa6..9d4e180e 100644 --- a/test/ui/showtimes/showtime_date_selector_test.dart +++ b/test/ui/showtimes/showtime_date_selector_test.dart @@ -42,18 +42,17 @@ void main() { testWidgets( 'when tapping a date, calls changeCurrentDate on the viewmodel with new date', (WidgetTester tester) async { + DateTime date; + when(mockViewModel.dates).thenReturn(dates); + when(mockViewModel.changeCurrentDate).thenReturn((newDate) => date = newDate); await _buildDateSelector(tester); - await tester.tap(find.text('Tue')); - DateTime newDateTime = - verify(mockViewModel.changeCurrentDate(typed(captureAny))).captured.single; - - expect(newDateTime.year, 2018); - expect(newDateTime.month, 1); - expect(newDateTime.day, 2); + expect(date.year, 2018); + expect(date.month, 1); + expect(date.day, 2); }, ); }); diff --git a/test/ui/theater_list/theater_list_test.dart b/test/ui/theater_list/theater_list_test.dart index acb79299..8d21e515 100644 --- a/test/ui/theater_list/theater_list_test.dart +++ b/test/ui/theater_list/theater_list_test.dart @@ -53,19 +53,17 @@ void main() { testWidgets( 'when theater tapped, should call both changeCurrentTheater and onTheaterTapped', (WidgetTester tester) async { + Theater theater; when(mockViewModel.currentTheater).thenReturn(theaters.first); when(mockViewModel.theaters).thenReturn(theaters); + when(mockViewModel.changeCurrentTheater) + .thenReturn((newTheater) => theater = newTheater); await _buildTheaterList(tester); - await tester.tap(find.text('Test Theater #2')); - Theater newTheater = verify( - mockViewModel.changeCurrentTheater(typed(captureAny))) - .captured - .first; - expect(newTheater.id, '2'); - expect(newTheater.name, 'Test Theater #2'); + expect(theater.id, '2'); + expect(theater.name, 'Test Theater #2'); expect(theaterTappedCallbackCalled, isTrue); }, From e0be47122fc65f0e1ae8efc9a94016314130e4f0 Mon Sep 17 00:00:00 2001 From: Iiro Krankka Date: Sat, 15 Sep 2018 16:43:28 +0300 Subject: [PATCH 2/3] Use image_test_utils for returning mocked image responses. --- pubspec.yaml | 1 + test/test_utils.dart | 58 ------------------- .../event_details_page_test.dart | 35 +++++------ test/ui/events/events_page_test.dart | 20 +++---- 4 files changed, 27 insertions(+), 87 deletions(-) delete mode 100644 test/test_utils.dart diff --git a/pubspec.yaml b/pubspec.yaml index 23e4cda8..3cf1f0f9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,7 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^3.0.0 + image_test_utils: ^1.0.0 flutter: uses-material-design: true diff --git a/test/test_utils.dart b/test/test_utils.dart deleted file mode 100644 index df4579a8..00000000 --- a/test/test_utils.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:async'; -import 'dart:io' as io; - -import 'package:mockito/mockito.dart'; - -/// This replaces the built-in HTTP client with a mocked one. The mocked client -/// will always return a transparent image. -/// -/// This is a workaround needed for widget tests that use network images, -/// otherwise the test will crash. -/// -/// For more context: -/// -/// * https://github.com/flutter/flutter/issues/13433 -/// * https://github.com/flutter/flutter_markdown/pull/17 -class TestHttpOverrides extends io.HttpOverrides { - io.HttpClient createHttpClient(io.SecurityContext context) { - return createMockImageHttpClient(context); - } -} - -class MockHttpClient extends Mock implements io.HttpClient {} -class MockHttpClientRequest extends Mock implements io.HttpClientRequest {} -class MockHttpClientResponse extends Mock implements io.HttpClientResponse {} -class MockHttpHeaders extends Mock implements io.HttpHeaders {} - -// Returns a mock HTTP client that responds with an image to all requests. -MockHttpClient createMockImageHttpClient(io.SecurityContext _) { - final MockHttpClient client = MockHttpClient(); - final MockHttpClientRequest request = MockHttpClientRequest(); - final MockHttpClientResponse response = MockHttpClientResponse(); - final MockHttpHeaders headers = MockHttpHeaders(); - - when(client.getUrl(any)).thenAnswer((_) => Future.value(request)); - when(request.headers).thenReturn(headers); - when(request.close()).thenAnswer((_) => Future.value(response)); - when(response.contentLength).thenReturn(_transparentImage.length); - when(response.statusCode).thenReturn(io.HttpStatus.ok); - when(response.listen(any)).thenAnswer((Invocation invocation) { - final void Function(List) onData = invocation.positionalArguments[0]; - final void Function() onDone = invocation.namedArguments[#onDone]; - final void Function(Object, [StackTrace]) onError = invocation.namedArguments[#onError]; - final bool cancelOnError = invocation.namedArguments[#cancelOnError]; - - return Stream>.fromIterable(>[_transparentImage]) - .listen(onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); - }); - - return client; -} - -const List _transparentImage = const [ - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, - 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, - 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44, - 0x41, 0x54, 0x78, 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0D, - 0x0A, 0x2D, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, -]; \ No newline at end of file diff --git a/test/ui/event_details/event_details_page_test.dart b/test/ui/event_details/event_details_page_test.dart index 37b37b7a..f18064f2 100644 --- a/test/ui/event_details/event_details_page_test.dart +++ b/test/ui/event_details/event_details_page_test.dart @@ -1,7 +1,6 @@ -import 'dart:io' as io; - import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:image_test_utils/image_test_utils.dart'; import 'package:inkino/models/actor.dart'; import 'package:inkino/models/event.dart'; import 'package:inkino/models/show.dart'; @@ -14,16 +13,12 @@ import 'package:inkino/ui/events/event_poster.dart'; import 'package:inkino/ui/events/event_poster.dart' as eventPoster; import 'package:meta/meta.dart'; -import '../../test_utils.dart'; - void main() { group('EventDetailsPage', () { String lastLaunchedTicketsUrl; String lastLaunchedTrailerUrl; setUp(() { - io.HttpOverrides.global = TestHttpOverrides(); - showtimeInfo.launchTicketsUrl = (url) => lastLaunchedTicketsUrl = url; eventPoster.launchTrailerVideo = (url) => lastLaunchedTrailerUrl = url; }); @@ -38,20 +33,22 @@ void main() { @required List trailers, @required Show show, }) { - return tester.pumpWidget(MaterialApp( - home: eventDetails.EventDetailsPage( - Event( - id: '1', - title: 'Test Title', - genres: 'Test Genres', - directors: [], - actors: [], - images: EventImageData.empty(), - youtubeTrailers: trailers, + return provideMockedNetworkImages(() async { + return tester.pumpWidget(MaterialApp( + home: eventDetails.EventDetailsPage( + Event( + id: '1', + title: 'Test Title', + genres: 'Test Genres', + directors: [], + actors: [], + images: EventImageData.empty(), + youtubeTrailers: trailers, + ), + show: show, ), - show: show, - ), - )); + )); + }); } testWidgets( diff --git a/test/ui/events/events_page_test.dart b/test/ui/events/events_page_test.dart index e10e53dc..b2c41a95 100644 --- a/test/ui/events/events_page_test.dart +++ b/test/ui/events/events_page_test.dart @@ -1,7 +1,6 @@ -import 'dart:io' as io; - import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:image_test_utils/image_test_utils.dart'; import 'package:inkino/models/actor.dart'; import 'package:inkino/models/event.dart'; import 'package:inkino/models/loading_status.dart'; @@ -13,9 +12,8 @@ import 'package:inkino/ui/events/events_page.dart'; import 'package:inkino/ui/events/events_page_view_model.dart'; import 'package:mockito/mockito.dart'; -import '../../test_utils.dart'; - class MockEventsPageViewModel extends Mock implements EventsPageViewModel {} + class MockNavigatorObserver extends Mock implements NavigatorObserver {} void main() { @@ -36,18 +34,20 @@ void main() { EventsPageViewModel mockViewModel; setUp(() { - io.HttpOverrides.global = TestHttpOverrides(); - observer = MockNavigatorObserver(); mockViewModel = MockEventsPageViewModel(); when(mockViewModel.refreshEvents).thenReturn(() {}); }); Future _buildEventsPage(WidgetTester tester) { - return tester.pumpWidget(MaterialApp( - home: EventsPageContent(mockViewModel), - navigatorObservers: [observer], - )); + return provideMockedNetworkImages(() { + return tester.pumpWidget( + MaterialApp( + home: EventsPageContent(mockViewModel), + navigatorObservers: [observer], + ), + ); + }); } testWidgets( From ed643213427ab86401d2848a89d94199dea465b0 Mon Sep 17 00:00:00 2001 From: Iiro Krankka Date: Sat, 15 Sep 2018 16:45:49 +0300 Subject: [PATCH 3/3] Revert creating redundant typedef for a date change callback. --- lib/ui/showtimes/showtime_page_view_model.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/ui/showtimes/showtime_page_view_model.dart b/lib/ui/showtimes/showtime_page_view_model.dart index 47a384bc..9d11da59 100644 --- a/lib/ui/showtimes/showtime_page_view_model.dart +++ b/lib/ui/showtimes/showtime_page_view_model.dart @@ -7,8 +7,6 @@ import 'package:inkino/redux/show/show_selectors.dart'; import 'package:meta/meta.dart'; import 'package:redux/redux.dart'; -typedef void DateChangeCallback(DateTime newDate); - class ShowtimesPageViewModel { ShowtimesPageViewModel({ @required this.status, @@ -23,7 +21,7 @@ class ShowtimesPageViewModel { final List dates; final DateTime selectedDate; final List shows; - final DateChangeCallback changeCurrentDate; + final Function(DateTime) changeCurrentDate; final Function refreshShowtimes; static ShowtimesPageViewModel fromStore(Store store) {