From c0f4d4bd4cd4cbc5dee5be50472f61814c5978f0 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Mon, 5 Apr 2021 22:46:24 -0600 Subject: [PATCH] Migrated testing to null-safety --- .../features/jokes/logic/jokes_provider.dart | 5 +- .../jokes/logic/jokes_state_notifier.dart | 4 +- lib/src/features/jokes/views/joke_page.dart | 1 - .../jokes/views/widgets/joke_available.dart | 12 +- .../logic/jokes_state_notifier_test.dart | 113 +++++++++++------- test/jokes/views/joke_page_test.dart | 112 ++++++++++------- 6 files changed, 151 insertions(+), 96 deletions(-) diff --git a/lib/src/features/jokes/logic/jokes_provider.dart b/lib/src/features/jokes/logic/jokes_provider.dart index 0027580..22a89d5 100644 --- a/lib/src/features/jokes/logic/jokes_provider.dart +++ b/lib/src/features/jokes/logic/jokes_provider.dart @@ -7,12 +7,13 @@ import 'package:jokes/jokes.dart'; import '../../../core/env/environment_config.dart'; import 'jokes_state.dart'; +export 'jokes_state.dart'; part 'jokes_state_notifier.dart'; /// Provider to use the JokesStateNotifier final jokesNotifierProvider = StateNotifierProvider( - (ref) => JokesNotifier(ref.watch(_getJokeProvider)), + (ref) => JokesNotifier(getJoke: ref.watch(getJokeProvider)), ); /// Repositories Providers @@ -31,7 +32,7 @@ final repositoryProvider = Provider( ); /// Use Cases Providers -final _getJokeProvider = Provider( +final getJokeProvider = Provider( (ref) { final repository = ref.watch(repositoryProvider); return GetJoke(repository: repository); diff --git a/lib/src/features/jokes/logic/jokes_state_notifier.dart b/lib/src/features/jokes/logic/jokes_state_notifier.dart index b1290da..6a0b2eb 100644 --- a/lib/src/features/jokes/logic/jokes_state_notifier.dart +++ b/lib/src/features/jokes/logic/jokes_state_notifier.dart @@ -4,9 +4,9 @@ part of 'jokes_provider.dart'; class JokesNotifier extends StateNotifier { /// Base constructor expects a [ProviderReference] to /// read its usecases and also defines inital state - JokesNotifier(GetJoke getJoke) + JokesNotifier({required GetJoke getJoke, JokesState? initialState}) : _getJoke = getJoke, - super(JokesState.initial()); + super(initialState ?? JokesState.initial()); final GetJoke _getJoke; diff --git a/lib/src/features/jokes/views/joke_page.dart b/lib/src/features/jokes/views/joke_page.dart index 4bf8e54..80e3020 100644 --- a/lib/src/features/jokes/views/joke_page.dart +++ b/lib/src/features/jokes/views/joke_page.dart @@ -5,7 +5,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/widgets/app_version.dart'; import '../logic/jokes_provider.dart'; -import '../logic/jokes_state.dart'; import 'joke_page.i18n.dart'; import 'widgets.dart'; diff --git a/lib/src/features/jokes/views/widgets/joke_available.dart b/lib/src/features/jokes/views/widgets/joke_available.dart index f16bebd..d3e57cb 100644 --- a/lib/src/features/jokes/views/widgets/joke_available.dart +++ b/lib/src/features/jokes/views/widgets/joke_available.dart @@ -8,8 +8,7 @@ class JokeCard extends StatelessWidget { const JokeCard({ Key? key, required this.joke, - }) : assert(joke != null), - super(key: key); + }) : super(key: key); final Joke joke; @@ -37,11 +36,10 @@ class JokeCard extends StatelessWidget { textAlign: TextAlign.center, ), const SizedBox(height: 30), - if (joke.safe != null) - Icon( - joke.safe ? Icons.check : Icons.remove, - color: joke.safe ? Colors.green : Colors.red, - ) + Icon( + joke.safe ? Icons.check : Icons.remove, + color: joke.safe ? Colors.green : Colors.red, + ) ], ), ), diff --git a/test/jokes/logic/jokes_state_notifier_test.dart b/test/jokes/logic/jokes_state_notifier_test.dart index 4659444..f3495de 100644 --- a/test/jokes/logic/jokes_state_notifier_test.dart +++ b/test/jokes/logic/jokes_state_notifier_test.dart @@ -1,56 +1,89 @@ import 'dart:async'; -import 'package:flutter_jokes/src/features/jokes/logic/jokes_state.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; import 'package:dartz/dartz.dart'; import 'package:errors/errors.dart'; import 'package:jokes/jokes.dart'; +import 'package:mocktail/mocktail.dart'; import '../../../lib/src/features/jokes/logic/jokes_provider.dart' - show JokesNotifier; + show JokesNotifier, JokesState; + +class GetJokeMock extends Mock implements GetJoke {} void main() { - test('test state of state notifier', () async { - //Setup - final _getJoke = GetJokeMock(); - - final joke = JokeModel( - category: 'Programing', - type: 'twopart', - setup: 'How do you generate a random string?', - delivery: 'Put a Windows user in front of Vim and tell him to exit.', - id: 10, - safe: true, - lang: 'en', - ); - - final tJokeStates = [ - JokesState.initial().toString(), - JokesState.loading().toString(), - JokesState(joke: joke).toString() - ]; - - when(_getJoke.call()).thenAnswer( - (_) => Future.value( - Right(joke), - ), - ); - - final _jokesNotifier = JokesNotifier(_getJoke); - - final List jokesStates = []; - _jokesNotifier.addListener((state) { - jokesStates.add(state.toString()); + group('JokesNotifier', () { + late GetJokeMock _getJoke; + + setUp(() { + _getJoke = GetJokeMock(); }); - ///Act - await _jokesNotifier.getJoke(); + test('Emit Initial, Loading, Data when successful', () async { + final joke = JokeModel( + category: 'Programing', + type: 'twopart', + setup: 'How do you generate a random string?', + delivery: 'Put a Windows user in front of Vim and tell him to exit.', + id: 10, + safe: true, + lang: 'en', + ); + + final tJokeStates = [ + JokesState.initial(), + JokesState.loading(), + JokesState.data(joke: joke) + ]; + + when(() => _getJoke()).thenAnswer( + (_) => Future.value( + Right(joke), + ), + ); + + final _jokesNotifier = JokesNotifier(getJoke: _getJoke); + + final List jokesStates = []; + _jokesNotifier.addListener((state) { + jokesStates.add(state); + }); + + ///Act + await _jokesNotifier.getJoke(); + + ///Expect + expect(jokesStates, tJokeStates); + }); - ///Expect - expect(jokesStates, tJokeStates); + test('Emit Initial, Loading, Error when Error Occurs', () async { + //Setup + + final tJokeStates = [ + JokesState.initial(), + JokesState.loading(), + JokesState.error(ServerFailure().toString()), + ]; + + when(() => _getJoke()).thenAnswer( + (_) => Future.value( + Left(ServerFailure()), + ), + ); + + final _jokesNotifier = JokesNotifier(getJoke: _getJoke); + + final List jokesStates = []; + _jokesNotifier.addListener((state) { + jokesStates.add(state); + }); + + ///Act + await _jokesNotifier.getJoke(); + + ///Expect + expect(jokesStates, tJokeStates); + }); }); } - -class GetJokeMock extends Mock implements GetJoke {} diff --git a/test/jokes/views/joke_page_test.dart b/test/jokes/views/joke_page_test.dart index c16ce01..0437ca6 100644 --- a/test/jokes/views/joke_page_test.dart +++ b/test/jokes/views/joke_page_test.dart @@ -1,68 +1,92 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:errors/errors.dart'; import 'package:jokes/jokes.dart'; +import 'package:mocktail/mocktail.dart'; import '../../../lib/src/app.dart'; import '../../../lib/src/features/jokes/logic/jokes_provider.dart'; import '../../../lib/src/features/jokes/views/joke_page.dart'; import '../../../lib/src/features/jokes/views/joke_page.i18n.dart'; -class MockRepository extends Mock implements IJokesRepository {} +class MockGetJoke extends Mock implements GetJoke {} void main() { final getJokeButton = find.byKey(getJokeButtonKey); + final getLoadingIndicator = find.byKey(loadingIndicatorKey); - testWidgets('Render default get joke message', (tester) async { - // Setup - await tester.pumpWidget(ProviderScope(child: JokesApp())); - //Expect - expect(find.text('${kTellJokeMessage.i18n}'), findsWidgets); - }); + group('JokesPage', () { + testWidgets('Render default get joke message', (tester) async { + // Setup + await tester.pumpWidget(ProviderScope(child: JokesApp())); + //Expect + expect(find.text('${kTellJokeMessage.i18n}'), findsWidgets); + }); + + testWidgets('Loading Indicator when LoadingState', (tester) async { + ///act + await tester.pumpWidget( + ProviderScope( + overrides: [ + jokesNotifierProvider.overrideWithProvider( + StateNotifierProvider( + (_) => JokesNotifier( + getJoke: MockGetJoke(), + initialState: JokesState.loading(), + ), + )) + ], + child: JokesApp(), + ), + ); + + //Validate initial state + expect(getLoadingIndicator, findsWidgets); + }); - testWidgets('Press button to get a joke', (tester) async { - //Setup - IJokesRepository mockRepository = MockRepository(); + testWidgets('Press button to get a joke', (tester) async { + //Setup + MockGetJoke _getJoke = MockGetJoke(); - final joke = JokeModel( - category: 'Programing', - type: 'twopart', - setup: 'How do you generate a random string?', - delivery: 'Put a Windows user in front of Vim and tell him to exit.', - id: 10, - safe: true, - lang: 'en', - ); + final joke = JokeModel( + category: 'Programing', + type: 'twopart', + setup: 'How do you generate a random string?', + delivery: 'Put a Windows user in front of Vim and tell him to exit.', + id: 10, + safe: true, + lang: 'en', + ); - when(mockRepository.getJoke()).thenAnswer( - (_) => Future.value( - Right(joke), - ), - ); + when(() => _getJoke()).thenAnswer( + (_) => Future.value( + Right(joke), + ), + ); - await tester.pumpWidget( - ProviderScope( - overrides: [ - repositoryProvider.overrideWithProvider( - Provider((ref) => mockRepository), - ) - ], - child: JokesApp(), - ), - ); + await tester.pumpWidget( + ProviderScope( + overrides: [ + getJokeProvider.overrideWithProvider( + Provider((ref) => _getJoke), + ) + ], + child: JokesApp(), + ), + ); - //Validate initial state - expect(find.text('${kTellJokeMessage.i18n}'), findsWidgets); - expect(getJokeButton, findsWidgets); + //Validate initial state + expect(find.text('${kTellJokeMessage.i18n}'), findsWidgets); + expect(getJokeButton, findsWidgets); - ///Act - await tester.tap(getJokeButton); - await tester.pump(); + ///Act + await tester.tap(getJokeButton); + await tester.pump(); - ///Validate new joke - expect(find.text('${kTellJokeMessage.i18n}'), findsNothing); - expect(find.text('How do you generate a random string?'), findsWidgets); + ///Validate new joke + expect(find.text('${kTellJokeMessage.i18n}'), findsNothing); + expect(find.text('How do you generate a random string?'), findsWidgets); + }); }); }