How to test a sealed state class in Flutter?

1.4k views Asked by At

I'm using Freezed to generate sealed data classes in my Flutter app. I need to test it and I don't know how to do so. Any idea? This is the state:

@freezed
abstract class LoginState with _$LoginState {
  const factory LoginState({
    @required Email email,
    @required Password password,
    @required bool isLoading,
    @required bool showErrors,
    @required Option<Either<AuthFailure, Unit>> failureOrSuccessOption,
  }) = _LoginState;

  factory LoginState.initial() => LoginState(
        failureOrSuccessOption: none(),
        isLoading: false,
        showErrors: false,
        email: Email(''),
        password: Password(''),
      );
}

This is the test I've been trying to run:

import 'package:dartz/dartz.dart';
import 'package:exchangecontrol/auth/application/login_cubit/login_cubit.dart';
import 'package:exchangecontrol/auth/domain/auth_repository.dart';
import 'package:mockito/mockito.dart';
import 'package:flutter_test/flutter_test.dart';

class MockLoginWithEmailAndPassword extends Mock implements AuthRepository {}

void main() {
  LoginCubit loginCubit;
  MockLoginWithEmailAndPassword mockLoginWithEmailAndPassword;

  setUp(() {
    mockLoginWithEmailAndPassword = MockLoginWithEmailAndPassword();
    loginCubit = LoginCubit(mockLoginWithEmailAndPassword);
  });
  test('Should be able to test login', () async {
    when(mockLoginWithEmailAndPassword.loginWithEmailAndPassword(any, any))
        .thenAnswer((_) async => right(unit));
    loginCubit.loginButtonPressed();
    expectLater(
      loginCubit,
      emitsInOrder(
        [
          loginCubit.state.copyWith(isLoading:true),
          loginCubit.state.copyWith(isLoading:false),
        ],
      ),
    );
  });
}

I want to check if, on executing the cubit loginMethod, it properly changes loading state to true (while getting a result) and the to false (after getting it), but I get this error message:

Expected: should do the following in order:
          * emit an event that _$_LoginState:<LoginState(email: Value(Left(EmailFailure.empty())), password: Value(Left(PasswordFailure.empty())), isLoading: true, showErrors: false, failureOrSuccessOption: None)>
          * emit an event that _$_LoginState:<LoginState(email: Value(Left(EmailFailure.empty())), password: Value(Left(PasswordFailure.empty())), isLoading: false, showErrors: false, failureOrSuccessOption: None)>
  Actual: _$_LoginState:<LoginState(email: Value(Left(EmailFailure.empty())), password: Value(Left(PasswordFailure.empty())), isLoading: false, showErrors: false, failureOrSuccessOption: None)>
   Which: was not a Stream or a StreamQueue
1

There are 1 answers

0
Mario Daglio On

You can use bloc_test which has a specific function for testing blocs

The snippet below is from a flutter app/toutorial that I am trying to complete with tests. The code below works for me.



    part of 'sign_in_form_bloc.dart';
    
    @freezed
    class SignInFormState with _$SignInFormState {
      const factory SignInFormState({
        required EmailAddress email,
        required Password password,
        required bool showErrorMessages,
        required bool isSubmitting,
        required Option> authFailureOrSuccessOption,
      }) = _SignInFormState;
    
      factory SignInFormState.initial() => SignInFormState(
            email: EmailAddress(''),
            password: Password(''),
            showErrorMessages: false,
            isSubmitting: false,
            authFailureOrSuccessOption: none(),
          );
    }


... this is part of the test file ...
    group('on SignInFormEvent.signInWithGooglePressed', () {
          blocTest(
              'emits submitting in progress first, then emits success, when signInWithGoogle succeeds',
              setUp: () {
                when(() => mockAuthFacade.signInWithGoogle())
                    .thenAnswer((_) async => right(unit));
              },
              build: () => SignInFormBloc(mockAuthFacade),
              act: (bloc) =>
                  bloc.add(const SignInFormEvent.signInWithGooglePressed()),
              expect: () => [
                    SignInFormState.initial().copyWith(
                      isSubmitting: true,
                      authFailureOrSuccessOption: none(),
                    ),
                    SignInFormState.initial().copyWith(
                      isSubmitting: false,
                      authFailureOrSuccessOption: some(right(unit)),
                    ),
                  ]);
          blocTest(
              'emits submitting in progress first, then emits failure, when signInWithGoogle fails',
              setUp: () {
                when(() => mockAuthFacade.signInWithGoogle()).thenAnswer(
                    (_) async => left(const AuthFailure.cancelledByUser()));
              },
              build: () => SignInFormBloc(mockAuthFacade),
              act: (bloc) =>
                  bloc.add(const SignInFormEvent.signInWithGooglePressed()),
              expect: () => [
                    SignInFormState.initial().copyWith(
                      isSubmitting: true,
                      authFailureOrSuccessOption: none(),
                    ),
                    SignInFormState.initial().copyWith(
                      isSubmitting: false,
                      authFailureOrSuccessOption:
                          some(left(const AuthFailure.cancelledByUser())),
                    ),
                  ]);
        });
...