3

I have two tests that check if a snack bar is showing when a login state is streamed and processed using the BlocListener.

void main() async {
  AuthenticationRepositoryMock _authenticationRepositoryMock;
  LoginBlocMock _loginBloc;
  final fireBaseUserMock = FirebaseUserMock();
  final randomValidPassword = "password";
  final buttonFinder = find.byKey(Key('credentials_button'));
  final snackBarFailureFinder = find.byKey(Key("snack_bar_failure"));
  final snackBarLoadingFinder = find.byKey(Key("snack_bar_loading"));
  final emailFieldFinder = find.byKey(Key('email_field'));
  final passwordFieldFinder = find.byKey(Key('password_field'));

  Widget makeTestableWidget() {
    return BlocProvider<LoginBloc>(
      builder: (context) => _loginBloc,
      child: MaterialApp(
        home: Scaffold(
          body: LoginPage(),
        )
      ),
    );
  }

  setUp((){
    _authenticationRepositoryMock = AuthenticationRepositoryMock();
    _loginBloc = LoginBlocMock(authenticationRepository: _authenticationRepositoryMock);
  });

  testWidgets('Show snack bar when state is LoginFailure', (WidgetTester tester) async {

    //Arrange
    var expectedStates = [
      LoginInitial(), 
      LoginFailure(error: "Could not find user. Please try different credentials")
    ];

    whenListen(_loginBloc, Stream.fromIterable(expectedStates));

    //Act
    await tester.pumpWidget(makeTestableWidget());

    expect(snackBarFailureFinder, findsNothing);

    await tester.enterText(emailFieldFinder, fireBaseUserMock.email);
    await tester.pumpAndSettle();

    await tester.enterText(passwordFieldFinder, randomValidPassword);
    await tester.pumpAndSettle();

    await tester.tap(buttonFinder);
    await tester.pumpAndSettle();

    //Assert
    expect(snackBarFailureFinder, findsOneWidget);
  });

  //FAILING FOR NO REASON!
  testWidgets('Show snack bar when state is LoginLoading', (WidgetTester tester) async {

    //Arrange
    var expectedStates = [
      LoginInitial(), 
      LoginLoading()
    ];

    whenListen(_loginBloc, Stream.fromIterable(expectedStates));

    //Act
    await tester.pumpWidget(makeTestableWidget());

    expect(snackBarLoadingFinder, findsNothing);

    await tester.enterText(emailFieldFinder, fireBaseUserMock.email);
    await tester.pumpAndSettle();

    await tester.enterText(passwordFieldFinder, randomValidPassword);
    await tester.pumpAndSettle();

    await tester.tap(buttonFinder);
    await tester.pumpAndSettle();

    //Assert
    expect(snackBarLoadingFinder, findsOneWidget);
  });
}

And these two tests are testing the following widget on the page:

BlocListener<LoginBloc, LoginState>(
              listener: (context, state){
                if(state is LoginFailure){
                  Scaffold.of(context)
                    .showSnackBar(SnackBar(
                      key: Key("snack_bar_failure"),
                      content: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [Text('Login Failure'), Icon(Icons.error)],
                      ),
                      backgroundColor: Colors.redAccent
                    ));
                }

                if (state is LoginLoading) {
                  Scaffold.of(context)
                    .showSnackBar(SnackBar(
                      key: Key("snack_bar_loading"),
                      content: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [Text('Logging in...'), Spinner()],
                      ),
                      backgroundColor: Colors.blueAccent
                    ));
                }
              }

The first test that looks up for the the snack bar with the key 'snack_bar_failure' passes, but the second test does not. It's literally the same test, same setup, only the expected states are different and the key of the snack bar is different snack_bar_loading.

The second test fails with the following error message:

The following TestFailure object was thrown running a test:
  Expected: exactly one matching node in the widget tree
  Actual: ?:<zero widgets with key [<'snack_bar_loading'>] (ignoring offstage widgets)>    
   Which: means none were found but one was expected

Am I missing something?

1 Answer 1

3

The question is answered in the issue (https://github.com/felangel/bloc/issues/655). Hope that helps!

Sign up to request clarification or add additional context in comments.

2 Comments

The only difference that I see from what you posted and my initial test is you used tester.pump() instead of tester.pumpAndSettle(). What is the difference between these two?
tester.pumpAndSettle repeatedly calls pump until there are no more scheduled frames whereas tester.pump only triggers a single frame. The main difference between what I posted and the original test is you had multiple pumpAndSettles which were both unnecessary and resulted in the SnackBar being shown and dismissed before the expect was executed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.