Writing unit tests using mockito and the @GenerateNiceMocks
annotation trying to test a cubit function, but none of the when().thenAnswer()
lines seem to be working. When debugging the test and checking the local variables of that function, all of them are set to null
instead of what was put in thenAnswer()
.
Function being tested
Future<void> loadStoredCredentials() async {
String? email = await storage.read(key: AuthenticationKeys.rememberEmail);
String? str = await storage.read(key: AuthenticationKeys.rememberMeKey);
String? lastViewedMessages = await storage.read(
key: 'lastViewedMessages',
);
bool rememberMe = false;
if (str != null) {
rememberMe = true;
}
if (email != null) {
emit(state.copyWith(
username: email,
usernameValid: FieldValidity.valid,
rememberMe: rememberMe,
lastViewedMessages: lastViewedMessages,
));
}
}
Test file
@GenerateNiceMocks([MockSpec<FlutterSecureStorage>()])
void main() async {
TestWidgetsFlutterBinding.ensureInitialized();
SignInCubit? signInCubit;
setUp(() async {
signInCubit = SignInCubit();
});
group('MockFlutterSecureStorage tests -', () {
setUpAll(() => FlutterSecureStorage.setMockInitialValues({}));
final email = Future.value('[email protected]');
final flag = Future.value('true');
final nullFuture = Future.value('');
blocTest<SignInCubit, SignInCubitState>(
'loadStoredCredentials - success',
build: () => SignInCubit(),
act: (cubit) async {
final storage = MockFlutterSecureStorage();
when(storage.read(key: AuthenticationKeys.rememberEmail))
.thenAnswer((_) => email);
when(storage.read(key: AuthenticationKeys.rememberMeKey))
.thenAnswer((_) => flag);
when(storage.read(key: 'lastViewedMessages'))
.thenAnswer((_) => nullFuture);
await cubit.loadStoredCredentials();
},
expect: () => (isA<SignInCubitState>),
);
});
}
Test error
Expected: <Closure: () => TypeMatcher<SignInCubitState> from Function 'isA': static.>
Actual: []
package:test_api expect
package:bloc_test/src/bloc_test.dart 220:16 testBloc.<fn>
===== asynchronous gap ===========================
dart:async _CustomZone.registerUnaryCallback
package:bloc_test/src/bloc_test.dart 201:7 testBloc.<fn>
dart:async runZonedGuarded
package:bloc_test/src/bloc_test.dart 199:9 testBloc
package:bloc_test/src/bloc_test.dart 156:13 blocTest.<fn>
The answer was optional dependency injection.
Function being tested
Future<void> loadStoredCredentials({FlutterSecureStorage? storage}) async {
FlutterSecureStorage _storage = storage ?? fSecureStorage;
String? email = await _storage.read(
key: AuthenticationKeys.rememberEmail,
);
String? str = await _storage.read(
key: AuthenticationKeys.rememberMeKey,
);
String? lastViewedMessages = await _storage.read(
key: AuthenticationKeys.lastViewed,
);
bool rememberMe = false;
if (str != null) {
rememberMe = true;
}
if (email != null) {
emit(state.copyWith(
username: email,
usernameValid: FieldValidity.valid,
rememberMe: rememberMe,
lastViewedMessages: lastViewedMessages,
));
}
}
Passing test
Switched to using mocktailx
instead of mockito
, hence syntax change
blocTest<SignInCubit, SignInCubitState>(
'loadStoredCredentials - success',
build: () => SignInCubit(),
act: (cubit) async {
when(() => mockStorage.read(key: AuthenticationKeys.rememberEmail))
.thenAnswerWith('[email protected]');
when(() => mockStorage.read(key: AuthenticationKeys.rememberMeKey))
.thenAnswerWith('true');
when(() => mockStorage.read(key: AuthenticationKeys.lastViewed))
.thenAnswerWith('time');
await cubit.loadStoredCredentials(storage: mockStorage);
},
expect: () => <SignInCubitState>[
const SignInCubitState(
username: '[email protected]',
usernameValid: FieldValidity.valid,
rememberMe: true,
lastViewedMessages: 'time',
),
],
);