我想对一个广播流回购进行单元测试.该回购首先生成StateOk,然后在调用onEvent()时生成StateError:

class Repo {
  final _controller = StreamController<State>.broadcast();

  Stream<State> get changes async* {
    yield StateOk();
    yield* _controller.stream;
  }

  Future<void> onEvent() async {
    _controller.add(StateError());
  }
}

abstract class State extends Equatable {
  @override
  List<Object?> get props => [];
}
class StateOk extends State {}
class StateError extends State {}

当我try 像这样运行单元测试时:

void main() {
  test('testError', () async {
    final Repo repo = Repo();
    expectLater(
        repo.changes,
        emitsInOrder([
          StateOk(),
          StateError(),
        ]));

    //await Future.delayed(const Duration(milliseconds: 1));
    repo.onEvent();
  });
}

测试无限期地运行.

如果我在调用onEvent之前添加Future.delayed(),测试将成功完成.所以,我首先假设这是一个时间问题,我们丢失了最初的StateOk流元素,这就是为什么expectLater没有完成.

但是,如果我删除emitsInOrder()中的StateError(),它就可以工作,而无需额外的等待时间.这告诉我,我们没有错过最初的状态. 怎么回事?

推荐答案

您的问题是您正在使用广播流.

当您获得changes流并侦听它时(这本身引入了异步延迟,因为async*函数在侦听流时不会立即启动),然后它首先发出一个StateOk()事件.只有after个已经交付,这可能需要一些时间,它有yield* _controller.stream个.

这意味着在监听流的expectLater(repo.changes, ...)之间,在达到yield*之前将有number个异步延迟.

如果_controller.streamyield*开始监听流之前emits StateError,那么你永远不会看到这个事件.这就是广播流的工作原理,如果你不及时收听,事件就会被广播到虚空中.

我会做的是让changes个人热切地行动起来,并与被要求的人同步.我可能会 Select 这样的东西:

  late final Stream<State> changes = Stream.multi((controller) {
    // Called when stream listened to.
    // StateOk sent asynchronously.
    controller.add(StateOk()); 
    // Listen to _controller.stream *right now*! Events buffered if needed.
    controller.addStream(_controller.stream).whenComplete(controller.closeSync);
  });

(如果原始流关闭,这也会关闭流,这可能是一个好主意,但测试需要期待它.

Dart相关问答推荐

基本数学函数的DART源代码

Dart - 检测未完成的期货

Flutter Getx - 未找到Xxx.您需要调用Get.put(Xxx()) - 但我已调用 Get.put(Xxx())

在 Flutter 中从树中临 timeshift 除时保留小部件状态

使用 2 个类型参数声明类型StateNotifierProvider,但给出了 1 个类型参数

在 Flutter 中检测键盘事件

执行 `dart2js` 时会生成哪些文件?为什么?

Dart JavaScript 与 jQuery 的互操作回调

Flutter更新导航栏

Flatter有数据绑定吗?

Dart - 一个 dart 项目如何在不使用 pub 的情况下从另一个 dart 项目导入代码?

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

Flitter中的谷歌 map 没有出现

如何从目录中获取文件列表并将其传递给ListView?

Error: The argument type "String?"can't be assigned to the parameter type"String" because "String?"s nullable and "String"isn't

如何在dart中生成随机字符串?

如何在 Dart 中读取控制台输入/标准输入?

Dart 中的 List.shuffle()?

如何展平flatten列表?

你如何在 Dart 中构建一个单例?