Flutter:如何使用blocTest对Bloc使用的计时器进行单元测试?



如何使用blocTest在Bloc内测试计时器?我正在使用bloc库,使用freeze来构建状态和事件对象(这里可能无关紧要,但谁知道呢)。

假设在我的block类中有这样的内容:

@override
Stream<MyState> mapEventToState(
MyEvent event,
) {
return event.when(
start: (value) async* {
yield state.copyWith(/* add state data */);
},
getStream: _myStream(),
);
}
Stream<MyState> _myStream() async* {
MyResult? result;
try {
final repo = await _repoProvider();
result = await repo.fetchResult();
} on Exception catch (e) {
_logger.e(e);
/* do some stuff irrelevant to the example */
}

Timer(const Duration(minutes: 1), () {
add(const MyEvent.getStream());
});
yield state.copyWith(
/* fill state object with data*/
);
}

因此,如果我的块接收到getStream事件,则调用_myStream()函数来处理释放。这个函数启动一个计时器,在1分钟后提交另一个getStream事件。我如何在单元测试中测试这一点,而不必等待一分钟(我使用块库的bloc_test为我的块编写blocTest函数。这带有等待功能,但它实际上只是等待提交的时间)?我试过用FakeAsync来解决这个问题,但是没有用——我总是只能注册第一个事件。我以为这样的东西会起作用,但它没有:blocTest

return TravelBloc(

mockRepoProvider,
mockTrackerProvider,
);
},
act: (bloc) =>
fakeAsync((async) {
bloc.add(const MyEvent.getStream());
async.elapse(Duration(minutes: 1, seconds: 1));
}),
expect: () => [
/* check state here */
],
verify: (_) {
verify(mockRepo.fetchResult).called(2);
});
在没有实际等待的情况下,是否有什么好的解决方案可以正确地测试这样的结构?fakeAsync似乎是正确的选择,但我认为自己无法将其与blocTest结合。

正如您所提到的,blocTest方法中的wait参数应该用于测试定时器、计数器等。然而,在您的情况下,这是不够的,因为您的单元测试执行起来太慢了。

您只需要应用DI原则(顺便说一下,当您向BLoC提供mock时,您已经在使用它了)并向TravelBloc的构造函数提供Duration对象。因此,从blocTest回调build将看起来像这样:

blocTest(
...
build: () => TravelBloc(
mockRepoProvider,
mockTrackerProvider,
updateInterval: tUpdateInterval,
),
...
);

当你需要"控制">

我遇到过一个类似的用例,我不得不在一个组中的几个地方延迟代码的执行,但我不希望这些延迟影响单元测试。我实现了PauserNonPauser类,后者是我只在测试中使用的no-op。

class Pauser {
const Pauser();
Future<void> pause300ms() async {
await Future.delayed(const Duration(milliseconds: 300));
}
Future<void> pause500ms() async {
await Future.delayed(const Duration(milliseconds: 500));
}
Future<void> pause5sec() async {
await Future.delayed(const Duration(seconds: 5));
}
}
class NonPauser implements Pauser {
const NonPauser();
@override
Future<void> pause300ms() async {
// no-op
}
@override
Future<void> pause500ms() async {
// no-op
}
@override
Future<void> pause5sec() async {
// no-op
}
}

然后在测试中通过build回调中的no-op实例:

blocTest<FooCubit, FooState>('given an initial state, when stuff is done, then the correct state is emitted',
build: () => FooCubit(pauser: const NonPauser()),
act: (bloc) => bloc.doStuff(),
expect: () => [FooStateImpl()]
);

cubit然后使用Pauser如下:

class FooCubit extends Cubit<FooState> {
FooCubit({Pauser pauser = const Pauser()})
: _pauser = pauser,
super(FooEmpty());
final Pauser _pauser;
Future<void> doStuff() async {
await _pauser.pause300ms();
_handleDo();
}
}

最新更新