Skip to content

Commit

Permalink
Updated unit tests for all AsyncNotifier subclasses
Browse files Browse the repository at this point in the history
# Conflicts:
#	ecommerce_app/test/src/features/authentication/presentation/account/account_screen_controller_test.dart
  • Loading branch information
bizz84 committed Aug 25, 2023
1 parent dd78e36 commit 4033732
Show file tree
Hide file tree
Showing 7 changed files with 410 additions and 188 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@Timeout(Duration(milliseconds: 500))
import 'package:ecommerce_app/src/features/authentication/data/fake_auth_repository.dart';
import 'package:ecommerce_app/src/features/authentication/presentation/account/account_screen_controller.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
Expand All @@ -7,56 +8,112 @@ import 'package:mocktail/mocktail.dart';
import '../../../../mocks.dart';

void main() {
late MockAuthRepository authRepository;
late AccountScreenController controller;
setUp(() {
authRepository = MockAuthRepository();
controller = AccountScreenController(
authRepository: authRepository,
ProviderContainer makeProviderContainer(MockAuthRepository authRepository) {
final container = ProviderContainer(
overrides: [
authRepositoryProvider.overrideWithValue(authRepository),
],
);
addTearDown(container.dispose);
return container;
}

setUpAll(() {
registerFallbackValue(const AsyncLoading<int>());
});

group('AccountScreenController', () {
test('initial state is AsyncValue.data', () {
test('initial state is AsyncData', () {
final authRepository = MockAuthRepository();
// create the ProviderContainer with the mock auth repository
final container = makeProviderContainer(authRepository);
// create a listener
final listener = Listener<AsyncValue<void>>();
// listen to the provider and call [listener] whenever its value changes
container.listen(
accountScreenControllerProvider,
listener,
fireImmediately: true,
);
// verify
verify(
// the build method returns a value immediately, so we expect AsyncData
() => listener(null, const AsyncData<void>(null)),
);
// verify that the listener is no longer called
verifyNoMoreInteractions(listener);
// verify that [signInAnonymously] was not called during initialization
verifyNever(authRepository.signOut);
expect(controller.state, const AsyncData<void>(null));
});

test('signOut success', () async {
// setup
when(authRepository.signOut).thenAnswer(
(_) => Future.value(),
);
// expect later
expectLater(
controller.stream,
emitsInOrder(const [
AsyncLoading<void>(),
AsyncData<void>(null),
]),
final authRepository = MockAuthRepository();
// stub method to return success
when(authRepository.signOut).thenAnswer((_) => Future.value());
// create the ProviderContainer with the mock auth repository
final container = makeProviderContainer(authRepository);
// create a listener
final listener = Listener<AsyncValue<void>>();
// listen to the provider and call [listener] whenever its value changes
container.listen(
accountScreenControllerProvider,
listener,
fireImmediately: true,
);
// sto
const data = AsyncData<void>(null);
// verify initial value from build method
verify(() => listener(null, data));
// run
final controller =
container.read(accountScreenControllerProvider.notifier);
await controller.signOut();
// verify
verifyInOrder([
// set loading state
// * use a matcher since AsyncLoading != AsyncLoading with data
// * https://codewithandrea.com/articles/unit-test-async-notifier-riverpod/
() => listener(data, any(that: isA<AsyncLoading>())),
// data when complete
() => listener(any(that: isA<AsyncLoading>()), data),
]);
verifyNoMoreInteractions(listener);
verify(authRepository.signOut).called(1);
});
test('signOut failure', () async {
// setup
final authRepository = MockAuthRepository();
// stub method to return success
final exception = Exception('Connection failed');
when(authRepository.signOut).thenThrow(exception);
// expect later
expectLater(
controller.stream,
emitsInOrder([
const AsyncLoading<void>(),
predicate<AsyncValue<void>>((value) {
expect(value.hasError, true);
return true;
}),
]),
// create the ProviderContainer with the mock auth repository
final container = makeProviderContainer(authRepository);
// create a listener
final listener = Listener<AsyncValue<void>>();
// listen to the provider and call [listener] whenever its value changes
container.listen(
accountScreenControllerProvider,
listener,
fireImmediately: true,
);
const data = AsyncData<void>(null);
// verify initial value from build method
verify(() => listener(null, data));
// run
final controller =
container.read(accountScreenControllerProvider.notifier);
await controller.signOut();
// verify
verifyInOrder([
// set loading state
// * use a matcher since AsyncLoading != AsyncLoading with data
() => listener(data, any(that: isA<AsyncLoading>())),
// error when complete
() => listener(
any(that: isA<AsyncLoading>()), any(that: isA<AsyncError>())),
]);
verifyNoMoreInteractions(listener);
verify(authRepository.signOut).called(1);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import 'package:mocktail/mocktail.dart';
import '../../../../mocks.dart';

void main() {
const testEmail = '[email protected]';
const testPassword = '1234';
const testFormType = EmailPasswordSignInFormType.signIn;

ProviderContainer makeProviderContainer(MockAuthRepository authRepository) {
final container = ProviderContainer(
overrides: [
Expand All @@ -19,9 +23,9 @@ void main() {
return container;
}

const testEmail = '[email protected]';
const testPassword = '1234';
const testFormType = EmailPasswordSignInFormType.signIn;
setUpAll(() {
registerFallbackValue(const AsyncLoading<int>());
});

group('EmailPasswordSignInController', () {
test('sign in success', () async {
Expand All @@ -32,24 +36,32 @@ void main() {
testPassword,
)).thenAnswer((_) => Future.value());
final container = makeProviderContainer(authRepository);
final controller =
container.read(emailPasswordSignInControllerProvider.notifier);
// expect later
expectLater(
controller.stream,
emitsInOrder([
const AsyncLoading<void>(),
const AsyncData<void>(null),
]),
final listener = Listener<AsyncValue<void>>();
container.listen(
emailPasswordSignInControllerProvider,
listener,
fireImmediately: true,
);
const data = AsyncData<void>(null);
// verify initial value from build method
verify(() => listener(null, data));
// run
final controller =
container.read(emailPasswordSignInControllerProvider.notifier);
final result = await controller.submit(
email: testEmail,
password: testPassword,
formType: testFormType,
);
// verify
expect(result, true);
verifyInOrder([
// set loading state
() => listener(data, any(that: isA<AsyncLoading>())),
// data when complete
() => listener(any(that: isA<AsyncLoading>()), data),
]);
verifyNoMoreInteractions(listener);
});
test('sign in failure', () async {
// setup
Expand All @@ -60,27 +72,32 @@ void main() {
testPassword,
)).thenThrow(exception);
final container = makeProviderContainer(authRepository);
final controller =
container.read(emailPasswordSignInControllerProvider.notifier);
// expect later
expectLater(
controller.stream,
emitsInOrder([
const AsyncLoading<void>(),
predicate<AsyncValue<void>>((state) {
expect(state.hasError, true);
return true;
}),
]),
final listener = Listener<AsyncValue<void>>();
container.listen(
emailPasswordSignInControllerProvider,
listener,
fireImmediately: true,
);
// verify initial value from build method
verify(() => listener(null, const AsyncData<void>(null)));
// run
final controller =
container.read(emailPasswordSignInControllerProvider.notifier);
final result = await controller.submit(
email: testEmail,
password: testPassword,
formType: testFormType,
);
// verify
expect(result, false);
verifyInOrder([
// set loading state
() => listener(
const AsyncData<void>(null), any(that: isA<AsyncLoading>())),
// error when complete
() => listener(
any(that: isA<AsyncLoading>()), any(that: isA<AsyncError>())),
]);
});
});
}
Loading

0 comments on commit 4033732

Please sign in to comment.