测试小部件时在颤动中测试 BlocListener 失败

Posted

技术标签:

【中文标题】测试小部件时在颤动中测试 BlocListener 失败【英文标题】:Test failing on BlocListener in flutter when testing widget 【发布时间】:2020-03-01 17:38:52 【问题描述】:

我有两个测试来检查在使用 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”的小吃店的第一个测试通过,但第二个测试没有通过。字面上是一样的测试,一样的设置,只是预期的状态不一样,小吃店的key不一样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

我错过了什么吗?

【问题讨论】:

【参考方案1】:

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

【讨论】:

我从您发布的内容和我的初始测试中看到的唯一区别是您使用了tester.pump() 而不是tester.pumpAndSettle()。这两者有什么区别? tester.pumpAndSettle 重复调用pump 直到没有更多的调度帧,而tester.pump 只触发一个帧。我发布的内容与原始测试之间的主要区别在于您有多个 pumpAndSettles,它们都是不必要的,并导致 SnackBar 在执行 expect 之前被显示和关闭。

以上是关于测试小部件时在颤动中测试 BlocListener 失败的主要内容,如果未能解决你的问题,请参考以下文章

找不到小部件键颤动测试

如何模拟用于测试颤动屏幕小部件的导航参数?

小部件中的图像 [颤动]

如何在颤动中从父小部件调用子小部件的initState()方法

如何在颤动中加载主小部件之前显示加载小部件?

如何在颤动中制作音频修剪小部件