测试小部件时,在 BlocListener 上测试失败

Test failing on BlocListener in flutter when testing widget

我有两个测试,检查在使用 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);
  });
}

这两个测试正在测试页面上的以下小部件:

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
                    ));
                }
              }

第一个用键 'snack_bar_failure' 查找小吃店的测试通过了,但第二个测试没有通过。简直是一样的测试,一样的设置,只是预期的状态不同,小吃店的钥匙不一样snack_bar_loading.

第二次测试失败并显示以下错误消息:

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

我错过了什么吗?

问题已在问题(https://github.com/felangel/bloc/issues/655)中得到解答。希望对您有所帮助!