考虑以下需要进行单元测试的代码

void run() {
    _activityRepo.activityUpdateStream.listen((token) async {
      await _userRepo.updateToken(token: token);
    });
  }

其中_activityRepo.activityUpdateStream是emits String个事件的Stream<String>.

这里的目标是测试每次_activityRepo.activityUpdateStream发出新事件时都会调用updateToken函数.单元测试目前类似于:

test('should update notification token when refreshed', () async {
    const fakePushToken = 'token';
    final fakeStream = StreamController<String>();

    when(() => liveActivityRepo.activityUpdateStream).thenAnswer((_) => fakeStream.stream);

    useCase.run();

    fakeStream.add(fakeUpdate);

    verify(() => userRepo.updateLiveActivityToken(token: fakePushToken)).called(1);
  });

此测试失败,因为在流侦听器内部的updateToken函数之前调用了verify.我应该如何重写此测试,以确保在运行测试中的验证步骤之前完全执行流侦听器?

推荐答案

此单元测试失败的原因是,正如您所解释的,verify是在流侦听器的异步操作之前执行的.

这些异步操作可能需要一些时间,因此依赖于某种计时器(包括那些处理超时的计时器).想想远程API调用吧.这些操作被放在称为事件队列的东西中,因为它们的完成依赖于发生的一些外部事件.

但它们可能只是一些延迟的、快速完成的操作,称为微任务.它们可能会令人困惑,因为它们看起来像同步操作,而我们可以预期它们会同步完成.它们被放入被称为微任务队列的东西中.

我不知道你的updateToken执行什么样的异步任务,所以我给你两个解决方案.

我举了这个例子:

import 'package:fake_async/fake_async.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';

main() {
  test("Sync with timers", () {
    int n = 0;
    var stream = Stream.fromIterable([1, 2, 3, 4, 5]);
    stream.listen((value) async {
      await Future.delayed(Duration(seconds: value));
      n = value;
      debugPrint(value.toString());
    });
    expect(n, 0);
  });

  test("Sync with microtasks", () {
    int n = 0;
    var stream = Stream.fromIterable([1, 2, 3, 4, 5]);
    stream.listen((value) async {
      n = value;
      debugPrint(value.toString());
    });
    expect(n, 0);
  });

  test("Fake async with timers", () {
    fakeAsync((fa) {
      int n = 0;
      var stream = Stream.fromIterable([1, 2, 3, 4, 5]);
      stream.listen((value) async {
        await Future.delayed(Duration(seconds: value));
        n = value;
        debugPrint(value.toString());
      });
      fa.flushTimers();
      expect(n, 5);
    });
  });

  test("Fake async with microtasks", () {
    fakeAsync((fa) {
      int n = 0;
      var stream = Stream.fromIterable([1, 2, 3, 4, 5]);
      stream.listen((value) async {
        n = value;
        debugPrint(value.toString());
      });
      fa.flushMicrotasks();
      expect(n, 5);
    });
  });
}

前两个测试显示了我们期望在同步单元测试中发生的事情,就像您所做的那样.在调用expect之前,n不会增加.第二个测试最令人惊讶,因为即使我们的"异步"任务实际上是同步的(它只是赋值并打印它),它也只在SYNC语句之后运行,包括expect.

通过使用fake_async包,您可以操作事件队列和微任务队列.您的单元测试嵌套在fakeAsync调用中,通过调用flushTimersflushMicrotasks,您可以完成流侦听器的所有异步操作.

请注意,如果将调用切换到flushTimersflushMicrotasks,fakeAsync测试将失败,这表明您需要调用其中的哪一个取决于异步任务的具体性质.

Dart相关问答推荐

使用arm64v8/DART Docker映像进行编译时不支持DART镜像

为什么 Dart 程序在使用词法作用域时会根据声明变量的位置而表现不同?

在 Flutter 中将 Widget 放在 ListView 之上

默认情况下不可为空(Non-nullable ):如何启用experiment?

在 Dart 中处理字节数组时,使用 Uint8List 优于 List 有什么优势?

如何在 Flutter 中更改 Html 小部件中的字体样式?

Flutter 以编程方式触发 FutureBuilder

如何在 Dart 中使用类型Aliases/Typedefs(也是非函数)?

如何在 Dart 语言中将 Future 转换为 List

如何更改手机屏幕上的Flutter应用程序名称显示?

如何判断 Dart NNBD 中的泛型类型是否可以为空?

从 HttpClientResponse 检索响应正文

如何在 Dart 中创建我们自己的metadata元数据?

在 Dart 中切换失败

如何提高数据与二进制数据转换的 Dart 性能?

Dart 会支持服务器端开发吗?

Dart 的输出是什么样的?

Dart 有没有办法测量小代码的执行时间

将`_`(即下划线)作为唯一参数传递给 Dart 语言函数是什么意思?

List firstWhere Bad state: No element