flutter - 如何在 flutter 小部件测试中捕获来自 future 的错误?

我在对 Future 期间抛出异常的小部件进行小部件测试时遇到问题。

重现问题的代码

这是一个简单的测试小部件,它以非常简单的方式重现问题(感谢 Remi Rousselet 对问题的简化)。

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();
    },
  ));
});

预期结果

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

══╡ 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
════════════════════════════════════════════════════════════════════════════════════════════════════

我的尝试

我已经尝试 expect 错误,如果它不在 Future 中我会这样做:

expect(tester.takeException(), equals(42));

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

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

编辑:

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

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

      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();
                },
              ),
            ],
          ),
        ));

      await tester.tap(find.text('GO'));
  });

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

最佳答案

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

来自 official documentation runAsync<T>

Runs a callback that performs real asynchronous work.

This is intended for callers who need to call asynchronous methods where the methods spawn isolates or OS threads and thus cannot be executed synchronously by calling 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();
        },
      ));

    });

  });

编辑:

(OP 提出的第二个问题)

在这种情况下使用

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

下面的完整片段

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'));});
});
});

编辑 2:

后来发现

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

没有被调用。

https://stackoverflow.com/questions/56600290/

相关文章:

gridview - Flutter GridView 页脚(用于指示无限滚动的负载)

dart - 为桌面构建 Flutter 应用程序

android - 如何使用 Flutter 构建增强现实应用程序?

android - Flutter - 导航到新屏幕,并清除所有以前的屏幕

flutter - 如何在 flutter 中获得唯一的设备ID?

dart - 使用 Flutter/dart 的 NTLM 身份验证

android - 是否可以在 flutter 中构建 android 小部件?

android - 如何在 Flutter 中获取 AppBar 高度

ios - 在 Flutter 中打开原生 UIViewController

google-maps - 如果可以使用 flutter 打开谷歌地图应用程序