如何让小部件测试等到 Bloc 更新状态?

Posted

技术标签:

【中文标题】如何让小部件测试等到 Bloc 更新状态?【英文标题】:How to make widget test wait until Bloc has updated the state? 【发布时间】:2020-05-10 03:45:19 【问题描述】:

我有一个EmailScreen(有状态小部件),它有一个文本输入和一个按钮。该按钮仅在输入有效电子邮件时启用。

我正在使用 Bloc,我的屏幕有 InitialEmailStateValidEmailInputState,当我运行应用程序时它运行良好。

在我的小部件测试中,第二个期望在 bloc 有机会更新状态之前失败:


  testWidgets('when valid email is input, button is enabled', (tester) async 
    const validEmail = 'email@provider.com';

    emailBloc.listen((event) 
      print('NEW EVENT: ' + event.toString());
    );

    await bootUpWidget(tester, emailScreen);

    final BottomButton button = tester.widget(
        find.widgetWithText(BottomButton, 'CONTINUE'));

    expect(button.enabled, isFalse);

    await tester.enterText(find.byType(TextInputScreen), validEmail);
    await tester.pumpAndSettle();

    expect(button.enabled, isTrue);
  );

这是我得到的输出:

NEW EVENT: InitialEmailState
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
  Expected: true
  Actual: <false>

...

The test description was:
  when valid email is input, button is enabled
════════════════════════════════════════════════════════════════════════════════════════════════════
Test failed. See exception logs above.
The test description was: when valid email is input, button is enabled

NEW EVENT: InputValidEmailState
✖ when valid email is input, button is enabled
Exited (1)

如您所见,它打印初始状态,第二次期望失败,然后打印期望状态。

提前致谢:)

== 更新 ==

我们通过在 main 开头添加 LiveTestWidgetsFlutterBinding(); 来实现此功能。但这感觉不是一个好的解决方案。

【问题讨论】:

【参考方案1】:

在使用 BLoC 驱动的 UI 小部件时遇到了同样的问题,我发现解决方案非常简单:使用 expectLater() 而不是 expect() 来等待 BLoC 状态更新:

testWidgets('test widgets driven by BLoC', (tester) async 
  await tester.pumpWidget(yourWidget);

  await tester.tap(loginButton); // Do something like tapping a button, entering text
  await tester.pumpAndSettle();

  await expectLater(findSomething, findsOneWidget); // IMPRTANT: use `expectLater` to wait for BLoC state update
  expect(findSomethingElse, findsOneWidget); // Subsequently you can use normal version `expect` until next `tester.pumpAndSettle()`

我在 BLoC 中设置了断点以找出问题所在,结果表明,在不使用 expectLater 的情况下,在 BLoC 流发出新状态之前正在评估正常的 expect。也就是说 BLoC 确实发出了一个新的状态,但是那个时候测试用例已经运行到最后了。

通过使用expectLater,在发出新状态后对其进行评估。

【讨论】:

以上是关于如何让小部件测试等到 Bloc 更新状态?的主要内容,如果未能解决你的问题,请参考以下文章

如何在更新父有状态小部件时更新子有状态小部件

无法使用颤振 Bloc 模式更新小部件外部的 UI

在 Flutter 中使用 BLoC - 在有状态小部件与无状态小部件中的使用

选择不依赖于状态的正确颤振块小部件

Flutter 状态管理(BloC):无状态与有状态小部件

python 让小部件保持最佳状态