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

Hil*_*tor 6 testing flutter bloc

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

\n\n

我正在使用 Bloc,我的屏幕上有InitialEmailStateValidEmailInputState,并且当我运行该应用程序时它工作正常。

\n\n

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

\n\n
\n  testWidgets(\'when valid email is input, button is enabled\', (tester) async {\n    const validEmail = \'email@provider.com\';\n\n    emailBloc.listen((event) {\n      print(\'NEW EVENT: \' + event.toString());\n    });\n\n    await bootUpWidget(tester, emailScreen);\n\n    final BottomButton button = tester.widget(\n        find.widgetWithText(BottomButton, \'CONTINUE\'));\n\n    expect(button.enabled, isFalse);\n\n    await tester.enterText(find.byType(TextInputScreen), validEmail);\n    await tester.pumpAndSettle();\n\n    expect(button.enabled, isTrue);\n  });\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是我得到的输出:

\n\n
NEW EVENT: InitialEmailState\n\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa1 EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK \xe2\x95\x9e\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\nThe following TestFailure object was thrown running a test:\n  Expected: true\n  Actual: <false>\n\n...\n\nThe test description was:\n  when valid email is input, button is enabled\n\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\nTest failed. See exception logs above.\nThe test description was: when valid email is input, button is enabled\n\nNEW EVENT: InputValidEmailState\n\xe2\x9c\x96 when valid email is input, button is enabled\nExited (1)\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您所看到的,它打印初始状态,失败第二个期望,然后打印期望状态。

\n\n

提前致谢 :)

\n\n

==更新==

\n\n

LiveTestWidgetsFlutterBinding();我们设法通过添加到 main 的开头来使其工作。但这感觉不是一个好的解决方案。

\n

小智 5

在 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()`
}
Run Code Online (Sandbox Code Playgroud)

我在 BLoC 中放置了断点来找出问题所在,事实证明,在不使用 的情况下,在 BLoC 流发出新状态之前正在评估expectLater法线。expect也就是说 BLoC 确实发出了一个新的状态,但此时测试用例已经运行到最后。

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