How to catch an error coming from a Future in flutter widget test?

Mul*_*dec 13 error-handling dart flutter flutter-test

I'm facing an issue while doing widget testing on a widget that throws an exception during a Future.

Code to reproduce the problem

Here's a simple testwidget that reproduces the problem in a very simple way (thanks to Remi Rousselet for the simplification of the problem).

testWidgets('This test should pass but fails', (tester) async {
  final future = Future<void>.error(42);

  await tester.pumpWidget(FutureBuilder(
    future: future,
    builder: (_, snapshot) {
      return Container();
    },
  ));
});
Run Code Online (Sandbox Code Playgroud)

Expected result

我希望测试能够顺利完成。相反,它失败并显示以下错误:

??? EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ?????????????????????????????????????????????????????
The number 42 was thrown running a test.

When the exception was thrown, this was the stack:
#2      main.<anonymous closure> (file:///C:/Projects/projet_65/mobile_app/test/ui/exception_test.dart:79:18)
#5      TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:0:0)
#8      TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:577:14)
#9      AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:993:24)
#15     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:990:15)
#16     testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:106:22)
#17     Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:168:27)
#20     Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:0:0)
#21     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:250:15)
#27     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:399:21)
(elided 17 frames from class _FakeAsync, package dart:async, and package dart:async-patch)

The test description was:
  This test should pass but fails
????????????????????????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

我尝试过的

我尝试expect过错误,就像如果不在Futurewith中一样:

??? EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ?????????????????????????????????????????????????????
The number 42 was thrown running a test.

When the exception was thrown, this was the stack:
#2      main.<anonymous closure> (file:///C:/Projects/projet_65/mobile_app/test/ui/exception_test.dart:79:18)
#5      TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:0:0)
#8      TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:577:14)
#9      AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:993:24)
#15     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:990:15)
#16     testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:106:22)
#17     Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:168:27)
#20     Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:0:0)
#21     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:250:15)
#27     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:399:21)
(elided 17 frames from class _FakeAsync, package dart:async, and package dart:async-patch)

The test description was:
  This test should pass but fails
????????????????????????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

但是该语句失败,并出现以下错误

expect(tester.takeException(), equals(42));
Run Code Online (Sandbox Code Playgroud)

编辑:

@shb答案对于暴露的情况是正确的。这是一个略微的修改,可以打破它并退回初始错误。这种情况在实际应用中更容易发生(对我来说就是这种情况)

The following TestFailure object was thrown running a test (but after the test had completed):
  Expected: 42
  Actual: null
Run Code Online (Sandbox Code Playgroud)

注意:我自愿忽略了@shb tester.runAsync提出的建议,以匹配最初的问题,因为它在特定情况下不起作用

shb*_*shb 8

用以下代码包装您的代码 await tester.runAsync(() async { .. }

官方文档 runAsync<T>

运行执行实际异步工作的回调。

这适用于需要调用异步方法的调用方,这些方法产生隔离或OS线程,因此无法通过调用pump同步执行。

见下文

testWidgets('This test should pass but fails', (tester) async {

    await tester.runAsync(() async {

      final future = Future<void>.error(42);

      await tester.pumpWidget(FutureBuilder(
        future: future,
        builder: (_, snapshot) {
          return Container();
        },
      ));

    });

  });
Run Code Online (Sandbox Code Playgroud)

通过测试

编辑:

(第二期OP)

在这种情况下使用

Future.delayed(Duration.zero, () {
   tester.tap(find.text('GO'));
});
Run Code Online (Sandbox Code Playgroud)

下面的完整代码段

testWidgets('2nd try This test should pass but fails', (tester) async {

Future future;
await tester.runAsync(() async {
  await tester.pumpWidget(
    MaterialApp(
      home: Row(
        children: <Widget>[
          FlatButton(
            child: const Text('GO'),
            onPressed: () {
              future = Future.error(42);
            },
          ),
          FutureBuilder(
            future: future,
            builder: (_, snapshot) {
              return Container();
            },
          ),
        ],
      ),
    ),
  );

  Future.delayed(Duration.zero, () {tester.tap(find.text('GO'));});
});
});
Run Code Online (Sandbox Code Playgroud)

屏幕截图

编辑2:

后来发现

Future.delayed(Duration.zero, () { tester.tap(find.text('GO')); });

没有被调用。


pas*_*ssy 6

期货向听众报告错误。如果a Future没有监听器,则会通知尚未Zone捕获的错误(src)。这是测试框架从中获取错误的地方。

解决这个错误的一种方法是在监听错误前等待听众Future

  testWidgets('This passes', (tester) async {
    final Completer completer = Completer();
    await tester.pumpWidget(FutureBuilder(
      future: completer.future,
      builder: (_, snapshot) {
        return Container();
      },
    ));

    // has subscribers, doesn't inform Zone about uncought error
    completer.completeError(42);
    tester.pumpAndSettle();
  });
Run Code Online (Sandbox Code Playgroud)