Skip to content

Commit a74fb3f

Browse files
committed
Updated unit tests for all AsyncNotifier subclasses
# Conflicts: # ecommerce_app/test/src/features/authentication/presentation/account/account_screen_controller_test.dart # Conflicts: # ecommerce_app/test/src/features/cart/presentation/add_to_cart/add_to_cart_controller_test.dart # Conflicts: # ecommerce_app/test/src/features/cart/presentation/add_to_cart/add_to_cart_controller_test.dart
1 parent 6d05ad3 commit a74fb3f

File tree

6 files changed

+332
-159
lines changed

6 files changed

+332
-159
lines changed

ecommerce_app/test/src/features/authentication/presentation/account/account_screen_controller_test.dart

Lines changed: 85 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// ignore: library_annotations
22
@Timeout(Duration(milliseconds: 500))
3+
import 'package:ecommerce_app/src/features/authentication/data/fake_auth_repository.dart';
34
import 'package:ecommerce_app/src/features/authentication/presentation/account/account_screen_controller.dart';
45
import 'package:flutter_riverpod/flutter_riverpod.dart';
56
import 'package:flutter_test/flutter_test.dart';
@@ -8,56 +9,112 @@ import 'package:mocktail/mocktail.dart';
89
import '../../../../mocks.dart';
910

1011
void main() {
11-
late MockAuthRepository authRepository;
12-
late AccountScreenController controller;
13-
setUp(() {
14-
authRepository = MockAuthRepository();
15-
controller = AccountScreenController(
16-
authRepository: authRepository,
12+
ProviderContainer makeProviderContainer(MockAuthRepository authRepository) {
13+
final container = ProviderContainer(
14+
overrides: [
15+
authRepositoryProvider.overrideWithValue(authRepository),
16+
],
1717
);
18+
addTearDown(container.dispose);
19+
return container;
20+
}
21+
22+
setUpAll(() {
23+
registerFallbackValue(const AsyncLoading<int>());
1824
});
25+
1926
group('AccountScreenController', () {
20-
test('initial state is AsyncValue.data', () {
27+
test('initial state is AsyncData', () {
28+
final authRepository = MockAuthRepository();
29+
// create the ProviderContainer with the mock auth repository
30+
final container = makeProviderContainer(authRepository);
31+
// create a listener
32+
final listener = Listener<AsyncValue<void>>();
33+
// listen to the provider and call [listener] whenever its value changes
34+
container.listen(
35+
accountScreenControllerProvider,
36+
listener.call,
37+
fireImmediately: true,
38+
);
39+
// verify
40+
verify(
41+
// the build method returns a value immediately, so we expect AsyncData
42+
() => listener(null, const AsyncData<void>(null)),
43+
);
44+
// verify that the listener is no longer called
45+
verifyNoMoreInteractions(listener);
46+
// verify that [signInAnonymously] was not called during initialization
2147
verifyNever(authRepository.signOut);
22-
expect(controller.state, const AsyncData<void>(null));
2348
});
2449

2550
test('signOut success', () async {
2651
// setup
27-
when(authRepository.signOut).thenAnswer(
28-
(_) => Future.value(),
29-
);
30-
// expect later
31-
expectLater(
32-
controller.stream,
33-
emitsInOrder(const [
34-
AsyncLoading<void>(),
35-
AsyncData<void>(null),
36-
]),
52+
final authRepository = MockAuthRepository();
53+
// stub method to return success
54+
when(authRepository.signOut).thenAnswer((_) => Future.value());
55+
// create the ProviderContainer with the mock auth repository
56+
final container = makeProviderContainer(authRepository);
57+
// create a listener
58+
final listener = Listener<AsyncValue<void>>();
59+
// listen to the provider and call [listener] whenever its value changes
60+
container.listen(
61+
accountScreenControllerProvider,
62+
listener.call,
63+
fireImmediately: true,
3764
);
65+
// sto
66+
const data = AsyncData<void>(null);
67+
// verify initial value from build method
68+
verify(() => listener(null, data));
3869
// run
70+
final controller =
71+
container.read(accountScreenControllerProvider.notifier);
3972
await controller.signOut();
4073
// verify
74+
verifyInOrder([
75+
// set loading state
76+
// * use a matcher since AsyncLoading != AsyncLoading with data
77+
// * https://codewithandrea.com/articles/unit-test-async-notifier-riverpod/
78+
() => listener(data, any(that: isA<AsyncLoading>())),
79+
// data when complete
80+
() => listener(any(that: isA<AsyncLoading>()), data),
81+
]);
82+
verifyNoMoreInteractions(listener);
4183
verify(authRepository.signOut).called(1);
4284
});
4385
test('signOut failure', () async {
4486
// setup
87+
final authRepository = MockAuthRepository();
88+
// stub method to return success
4589
final exception = Exception('Connection failed');
4690
when(authRepository.signOut).thenThrow(exception);
47-
// expect later
48-
expectLater(
49-
controller.stream,
50-
emitsInOrder([
51-
const AsyncLoading<void>(),
52-
predicate<AsyncValue<void>>((value) {
53-
expect(value.hasError, true);
54-
return true;
55-
}),
56-
]),
91+
// create the ProviderContainer with the mock auth repository
92+
final container = makeProviderContainer(authRepository);
93+
// create a listener
94+
final listener = Listener<AsyncValue<void>>();
95+
// listen to the provider and call [listener] whenever its value changes
96+
container.listen(
97+
accountScreenControllerProvider,
98+
listener.call,
99+
fireImmediately: true,
57100
);
101+
const data = AsyncData<void>(null);
102+
// verify initial value from build method
103+
verify(() => listener(null, data));
58104
// run
105+
final controller =
106+
container.read(accountScreenControllerProvider.notifier);
59107
await controller.signOut();
60108
// verify
109+
verifyInOrder([
110+
// set loading state
111+
// * use a matcher since AsyncLoading != AsyncLoading with data
112+
() => listener(data, any(that: isA<AsyncLoading>())),
113+
// error when complete
114+
() => listener(
115+
any(that: isA<AsyncLoading>()), any(that: isA<AsyncError>())),
116+
]);
117+
verifyNoMoreInteractions(listener);
61118
verify(authRepository.signOut).called(1);
62119
});
63120
});

ecommerce_app/test/src/features/authentication/presentation/sign_in/email_password_sign_in_controller_test.dart

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import 'package:mocktail/mocktail.dart';
1010
import '../../../../mocks.dart';
1111

1212
void main() {
13+
const testEmail = '[email protected]';
14+
const testPassword = '1234';
15+
const testFormType = EmailPasswordSignInFormType.signIn;
16+
1317
ProviderContainer makeProviderContainer(MockAuthRepository authRepository) {
1418
final container = ProviderContainer(
1519
overrides: [
@@ -20,9 +24,9 @@ void main() {
2024
return container;
2125
}
2226

23-
const testEmail = '[email protected]';
24-
const testPassword = '1234';
25-
const testFormType = EmailPasswordSignInFormType.signIn;
27+
setUpAll(() {
28+
registerFallbackValue(const AsyncLoading<int>());
29+
});
2630

2731
group('EmailPasswordSignInController', () {
2832
test('sign in success', () async {
@@ -33,24 +37,32 @@ void main() {
3337
testPassword,
3438
)).thenAnswer((_) => Future.value());
3539
final container = makeProviderContainer(authRepository);
36-
final controller =
37-
container.read(emailPasswordSignInControllerProvider.notifier);
38-
// expect later
39-
expectLater(
40-
controller.stream,
41-
emitsInOrder([
42-
const AsyncLoading<void>(),
43-
const AsyncData<void>(null),
44-
]),
40+
final listener = Listener<AsyncValue<void>>();
41+
container.listen(
42+
emailPasswordSignInControllerProvider,
43+
listener.call,
44+
fireImmediately: true,
4545
);
46+
const data = AsyncData<void>(null);
47+
// verify initial value from build method
48+
verify(() => listener(null, data));
4649
// run
50+
final controller =
51+
container.read(emailPasswordSignInControllerProvider.notifier);
4752
final result = await controller.submit(
4853
email: testEmail,
4954
password: testPassword,
5055
formType: testFormType,
5156
);
5257
// verify
5358
expect(result, true);
59+
verifyInOrder([
60+
// set loading state
61+
() => listener(data, any(that: isA<AsyncLoading>())),
62+
// data when complete
63+
() => listener(any(that: isA<AsyncLoading>()), data),
64+
]);
65+
verifyNoMoreInteractions(listener);
5466
});
5567
test('sign in failure', () async {
5668
// setup
@@ -61,27 +73,32 @@ void main() {
6173
testPassword,
6274
)).thenThrow(exception);
6375
final container = makeProviderContainer(authRepository);
64-
final controller =
65-
container.read(emailPasswordSignInControllerProvider.notifier);
66-
// expect later
67-
expectLater(
68-
controller.stream,
69-
emitsInOrder([
70-
const AsyncLoading<void>(),
71-
predicate<AsyncValue<void>>((state) {
72-
expect(state.hasError, true);
73-
return true;
74-
}),
75-
]),
76+
final listener = Listener<AsyncValue<void>>();
77+
container.listen(
78+
emailPasswordSignInControllerProvider,
79+
listener.call,
80+
fireImmediately: true,
7681
);
82+
// verify initial value from build method
83+
verify(() => listener(null, const AsyncData<void>(null)));
7784
// run
85+
final controller =
86+
container.read(emailPasswordSignInControllerProvider.notifier);
7887
final result = await controller.submit(
7988
email: testEmail,
8089
password: testPassword,
8190
formType: testFormType,
8291
);
8392
// verify
8493
expect(result, false);
94+
verifyInOrder([
95+
// set loading state
96+
() => listener(
97+
const AsyncData<void>(null), any(that: isA<AsyncLoading>())),
98+
// error when complete
99+
() => listener(
100+
any(that: isA<AsyncLoading>()), any(that: isA<AsyncError>())),
101+
]);
85102
});
86103
});
87104
}

ecommerce_app/test/src/features/cart/presentation/add_to_cart/add_to_cart_controller_test.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ void main() {
4545
);
4646
// item quantity controller
4747
final itemQuantityController =
48-
container.read(itemQuantityProvider.notifier);
48+
container.read(itemQuantityControllerProvider.notifier);
4949
final itemQuantityListener = Listener<int>();
5050
container.listen(
51-
itemQuantityProvider,
51+
itemQuantityControllerProvider,
5252
itemQuantityListener.call,
5353
fireImmediately: true,
5454
);
@@ -58,7 +58,7 @@ void main() {
5858
verify(() => addToCartListener(null, initialData));
5959
verify(() => itemQuantityListener(null, 1));
6060
// update quantity
61-
itemQuantityController.state = quantity;
61+
itemQuantityController.updateQuantity(quantity);
6262
// the quantity is updated
6363
verify(() => itemQuantityListener(1, quantity));
6464
// add item
@@ -103,10 +103,10 @@ void main() {
103103
);
104104
// item quantity controller
105105
final itemQuantityController =
106-
container.read(itemQuantityProvider.notifier);
106+
container.read(itemQuantityControllerProvider.notifier);
107107
final itemQuantityListener = Listener<int>();
108108
container.listen(
109-
itemQuantityProvider,
109+
itemQuantityControllerProvider,
110110
itemQuantityListener.call,
111111
fireImmediately: true,
112112
);
@@ -116,7 +116,7 @@ void main() {
116116
verify(() => addToCartListener(null, initialData));
117117
verify(() => itemQuantityListener(null, 1));
118118
// update quantity
119-
itemQuantityController.state = quantity;
119+
itemQuantityController.updateQuantity(quantity);
120120
// the quantity is updated
121121
verify(() => itemQuantityListener(1, quantity));
122122
// add item

0 commit comments

Comments
 (0)