对广播流类进行单元测试

问题描述 投票:0回答:1

我想对广播流存储库进行单元测试。该存储库首先生成 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 {}

当我尝试运行这样的单元测试时:

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(),它就可以工作而无需额外的等待时间。这告诉我,我们并没有错过最初的 StateOk。 这是怎么回事?

dart unit-testing async-await streaming
1个回答
1
投票

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

当您获取

changes
流并监听它时(这本身会引入异步延迟,因为在监听流时
async*
函数不会立即启动),那么它首先会发出
StateOk()
事件。只有在交付之后(可能需要一些时间),它才会yield* _controller.stream

这意味着在执行

expectLater(repo.changes, ...)

 (监听流)之间,在达到 
yield* 之前,将会有 number
 的异步延迟。

如果在

StateError

 开始侦听流之前由 
_controller.stream
 发出 
yield*
,那么您将永远不会看到该事件。这就是广播流的工作原理,如果你不及时收听,事件就会被广播到虚空。

我要做的就是让

changes

热切地、同步地行动以被倾听。我可能会选择类似的东西:

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); });
(如果原始流关闭,这也会关闭流,这可能是一个好主意,但测试需要预料到它。)

© www.soinside.com 2019 - 2024. All rights reserved.