Search code examples
flutterdartunit-testingmockito

when().thenAnswer() not firing in blocTest


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>

Solution

  • 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',
        ),
      ],
    );