我想对广播流存储库进行单元测试。该存储库首先生成 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。 这是怎么回事?
您的问题是您正在使用广播流。
当您获取
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);
});
(如果原始流关闭,这也会关闭流,这可能是一个好主意,但测试需要预料到它。)