【问题标题】:Why does an unawaited Future error make my test fail?为什么未等待的 Future 错误会使我的测试失败?
【发布时间】:2021-11-25 18:09:21
【问题描述】:

鉴于这个最小的测试

      test('ignore failures', () {
        Future<void> failingFunc() async {
          throw Exception('BOOOOM');
        }

        failingFunc();
      });

测试失败,输出如下:

Exception: BOOOOM
main.<fn>.<fn>.<fn>.failingFunc
test/…/auth/auth_action_bloc_test.dart:374
main.<fn>.<fn>.<fn>
test/…/auth/auth_action_bloc_test.dart:377
===== asynchronous gap ===========================
dart:async                                                 _AsyncAwaitCompleter.completeError
test/unit/features/auth/auth_action_bloc_test.dart         main.<fn>.<fn>.<fn>.failingFunc
test/…/auth/auth_action_bloc_test.dart:1
main.<fn>.<fn>.<fn>
test/…/auth/auth_action_bloc_test.dart:377

不明白为什么该测试会失败。

我的理解是 failingFunc() 会“静默”失败,因为它没有等待。 (即异常将被抛出“其他地方”,并被忽略) 我(几乎)确信在生产代码中这样的事情会正常工作。

以下测试确实以我期望的方式失败(即添加 await ):

      test('ignore failures', () async {
        Future<void> failingFunc() async {
          throw Exception('BOOOOM');
        }

        await failingFunc();
      });

它按预期失败了

Exception: BOOOOM
main.<fn>.<fn>.<fn>.failingFunc
test/…/auth/auth_action_bloc_test.dart:374
main.<fn>.<fn>.<fn>
test/…/auth/auth_action_bloc_test.dart:377

【问题讨论】:

    标签: dart testing async-await


    【解决方案1】:

    异常会在其他地方抛出,但肯定不会被忽略。

    将一个未捕获的异步错误报告给当前Zone 的未捕获错误处理程序。如果你没有做任何特别的事情,那就是根区域,它会在出现未捕获的错误时终止程序。

    嗯,它会在本机代码中这样做。在网络上,您不能使浏览器崩溃,因此它只会被视为未处理的 JavaScript 错误,可能会或可能不会被忽略。这仍然是一种糟糕的风格,并且使调试变得更加困难,因此您不应该让错误在生产中未经处理。

    test 包使用未捕获错误处理程序在新区域中运行每个测试,因此它知道哪个测试导致了异常。它还认识到错误发生在它已经认为测试完成并成功之后。如果幸运的话,它会返回并将测试标记为因抛出该错误而失败,即使它稍后会这样做。如果运气不好,速度慢,并且所有测试都已完成并且测试运行器已打印结果,那么返回并更改结果可能为时已晚。

    例子:

    import "package:test/test.dart";
    
    void main() {
      test("example", () {
        Future.delayed(Duration(seconds: 1), () => throw "error");
      });
      test("delayed", () => Future.delayed(Duration(seconds: 2)));
    }
    

    在此示例中,test 包识别出"example" 测试失败,即使它在返回并假装一切正常后还是失败了。 如果您将"delayed" 的持续时间更改为零,那么它会在第一个测试未捕获错误发生之前报告两个测试都成功,并且它只会打印00:00 +2: All tests passed!

    如果你真的想要忽略未来的错误,你必须做点什么。 从 Dart 2.14 开始,你可以这样写:

     failingFunc().ignore();
    

    ignore() 是在dart:async/dart:core 中声明的Future 的扩展方法,它捕获并忽略来自未来的错误 它被调用了。 小心使用,错误可能很严重。

    【讨论】:

    • 非常感谢您的解释 :) 那么对于您的示例,这意味着可能运行文件的单个测试,或者运行它们可能不会给出相同的结果?
    • 是的。单个测试应该在它返回时完成,或者,如果它返回一个未来,那么当该未来完成时。在此之后测试内部不会发生任何事情(出于多种原因,其中一个原因是 tearDown 已被调用,并且此后可能会运行一个或多个其他测试的 setUp。共享变量非常重创)。因此,在那之后处理完成的测试抛出,或者做任何事情,都是非常“尽力而为”的,没有保证的结果,这是你应该尽量避免的事情。不要留下悬而未决的未来。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-04
    • 1970-01-01
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多