当tapped小部件启动Timer时,如何运行单元测试?

时间:2017-02-24 21:15:20

标签: unit-testing flutter

我有一个Widget,当点击它时,保存一个ID号,覆盖一个CircularProgressIndicator 1000ms然后弹出进度指示器并将用户路由到另一个页面。

这个带有ProgressIndicator和Timer的位是新的,并且已经破坏了我的单元测试,现在它给了我以下错误:

The following assertion was thrown running a test:
'package:flutter_test/src/binding.dart': Failed assertion: line 574 pos 12: '() {
      'A Timer is still pending even after the widget tree was disposed.';
      return _fakeAsync.nonPeriodicTimerCount == 0;
    }': is not true.

Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new

When the exception was thrown, this was the stack:
#2      AutomatedTestWidgetsFlutterBinding._verifyInvariants (package:flutter_test/src/binding.dart:574:12)
#3      TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:415:7)
<asynchronous suspension>
#7      TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:392:14)
#8      AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:549:24)
#14     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:547:16)
#15     testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:54:50)
#16     Declarer.test.<anonymous closure>.<anonymous closure> (package:test/src/backend/declarer.dart:131:19)
<asynchronous suspension>
#17     Invoker.waitForOutstandingCallbacks.<anonymous closure>.<anonymous closure> (package:test/src/backend/invoker.dart:200:17)
<asynchronous suspension>
#22     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test/src/backend/invoker.dart:197:7)
#26     Invoker.waitForOutstandingCallbacks (package:test/src/backend/invoker.dart:196:5)
#27     Declarer.test.<anonymous closure> (package:test/src/backend/declarer.dart:129:29)
<asynchronous suspension>
#28     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test/src/backend/invoker.dart:322:23)
<asynchronous suspension>
#43     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:385)
#44     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:414)
#45     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:148)
(elided 31 frames from class _AssertionError, class _FakeAsync, package dart:async, package
dart:async-patch, and package stack_trace)

这就是破损测试的样子:

testWidgets('Tapping item saves its id', (WidgetTester tester) async {
  await launchApp(item, tester);
  await tester.tap(find.byConfig(item));

  expect(sameId(global_state.currentId, item.id), isTrue);
});

在处置小部件树之前是否有某种方法来引入延迟?我还有什么其他选择?或者我错了问题的根源?

4 个答案:

答案 0 :(得分:3)

testWidgets会自动引入一个FakeAsync区域,让您可以跨越时间。使用pump推进时间。

您看到的错误是因为您的小部件在处理时未取消定时器。确保您的Widget对象永远不会分配资源(如计时器),并且您的State对象始终使用dispose方法清理它们分配的所有资源。

答案 1 :(得分:1)

您应该可以在Quiver的FakeAsync库的FakeAsync中执行此操作: https://www.dartdocs.org/documentation/quiver/0.24.0/quiver.testing.async/FakeAsync-class.html

在Flutter框架本身的测试中有许多这种模式的例子: https://github.com/flutter/flutter/search?utf8=%E2%9C%93&q=FakeAsync

答案 2 :(得分:0)

伊恩的答案是正确的,但是如果有人在寻找代码,我会推荐这个答案

https://stackoverflow.com/a/54021863/8411356

如果这不起作用,则上述答案中的主题也很有帮助。

答案 3 :(得分:0)

使用 await tester.pumpAndSettle(),默认使用 100 毫秒延迟,但您可以添加自己的值:await tester.pumpAndSettle(const Duration(seconds:1))

如果您测试使用 Timer 或一些引擎盖下的动画小部件的小部件,它会很有用。

这是一个如何使用它的小例子:

testWidgets('build with $state state', (tester) async {
        when(() => bloc.state).thenReturn(state);
        await blocTester.test(
          tester: tester,
          stateConfig: states[state]!,
        );
        await tester.pumpAndSettle();
      })