Skip to content

Commit

Permalink
Migrated testing to null-safety
Browse files Browse the repository at this point in the history
  • Loading branch information
elianortega committed Apr 6, 2021
1 parent f095447 commit c0f4d4b
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 96 deletions.
5 changes: 3 additions & 2 deletions lib/src/features/jokes/logic/jokes_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<JokesNotifier, JokesState>(
(ref) => JokesNotifier(ref.watch(_getJokeProvider)),
(ref) => JokesNotifier(getJoke: ref.watch(getJokeProvider)),
);

/// Repositories Providers
Expand All @@ -31,7 +32,7 @@ final repositoryProvider = Provider<IJokesRepository>(
);

/// Use Cases Providers
final _getJokeProvider = Provider<GetJoke>(
final getJokeProvider = Provider<GetJoke>(
(ref) {
final repository = ref.watch(repositoryProvider);
return GetJoke(repository: repository);
Expand Down
4 changes: 2 additions & 2 deletions lib/src/features/jokes/logic/jokes_state_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ part of 'jokes_provider.dart';
class JokesNotifier extends StateNotifier<JokesState> {
/// 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;

Expand Down
1 change: 0 additions & 1 deletion lib/src/features/jokes/views/joke_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
12 changes: 5 additions & 7 deletions lib/src/features/jokes/views/widgets/joke_available.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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,
)
],
),
),
Expand Down
113 changes: 73 additions & 40 deletions test/jokes/logic/jokes_state_notifier_test.dart
Original file line number Diff line number Diff line change
@@ -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 = <String>[
JokesState.initial().toString(),
JokesState.loading().toString(),
JokesState(joke: joke).toString()
];

when(_getJoke.call()).thenAnswer(
(_) => Future.value(
Right<Failure, JokeModel>(joke),
),
);

final _jokesNotifier = JokesNotifier(_getJoke);

final List<String> 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>[
JokesState.initial(),
JokesState.loading(),
JokesState.data(joke: joke)
];

when(() => _getJoke()).thenAnswer(
(_) => Future.value(
Right<Failure, Joke>(joke),
),
);

final _jokesNotifier = JokesNotifier(getJoke: _getJoke);

final List<JokesState> 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>[
JokesState.initial(),
JokesState.loading(),
JokesState.error(ServerFailure().toString()),
];

when(() => _getJoke()).thenAnswer(
(_) => Future.value(
Left<Failure, Joke>(ServerFailure()),
),
);

final _jokesNotifier = JokesNotifier(getJoke: _getJoke);

final List<JokesState> jokesStates = [];
_jokesNotifier.addListener((state) {
jokesStates.add(state);
});

///Act
await _jokesNotifier.getJoke();

///Expect
expect(jokesStates, tJokeStates);
});
});
}

class GetJokeMock extends Mock implements GetJoke {}
112 changes: 68 additions & 44 deletions test/jokes/views/joke_page_test.dart
Original file line number Diff line number Diff line change
@@ -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, JokesState>(
(_) => 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<Failure, JokeModel>(joke),
),
);
when(() => _getJoke()).thenAnswer(
(_) => Future.value(
Right<Failure, Joke>(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);
});
});
}

0 comments on commit c0f4d4b

Please sign in to comment.