接续上一篇介绍测试,之前也提到过Reactor提供VirtualTimeScheduler
来让测试更方便,现在就来结合StepVerifier
与VirtualTimeScheduler
看看会有甚麽火花。
测试使用StepVerifier
传入publisher
的方法,除了常用到的create
来接收之外,withVirtualTime
也是一个好方法,尤其是当你的情境是与时间有关的时候,从原始码可以看到,有别於create
是直接传入一个publisher
,withVirtualTime
需要用Supplier
包住publisher
後再传入,原因是时间相关的operators通常是透过Schedulers.parallel()
,如果要使用虚拟时间就要用VirtualTimeScheduler
去取代掉原本的Scheduler
,所以VirtualTimeScheduler
必须在operators初始化之前就已经启动,也就是要延後operators初始化的时间,这句话基本上就等於lazy loading,所以为了让虚拟时间的机制成功,必须在Supplier
内初始化Flux
,而不是在其他的地方。
static <T> FirstStep<T> withVirtualTime(Supplier<? extends Publisher<? extends T>> scenarioSupplier) {
return withVirtualTime(scenarioSupplier, Long.MAX_VALUE);
}
StepVerifier
提供了两个方法让你可以控制时间增加
thenAwait(Duration)
:暂停期望评估(expectation evaluation),停下来等相对就是时间增加(可以顺便反思人生不进则退,时光飞逝......)expectNoEvent(Duration)
:在传入的Duration期间内预期没有任何事情发生,相对也是一个时间推进的概念。要注意subscription
也被视做一个event,如果一开始预期就没有event发生,建议使用expectSubscription().expectNoEvent(duration)
,要不然都会是错误。withVirtualTime
的同时必须要用到上述的两种方法去增加时间,否则时间就将会停滞。从官方提供的范例可以看到,遇到delay
建议就使用expectSubscription
先起手,避免subscription
这个事件导致expectNoEvent
失败。透过VirtualTime
推进了一天之後得到了一个资料0L,整个测试过程在真实世界只过了不到1秒。
StepVerifier.withVirtualTime(() -> Mono.delay(Duration.ofDays(1)))
.expectSubscription()
.expectNoEvent(Duration.ofDays(1))
.expectNext(0L)
.verifyComplete();
在研究的时候找到一个stackoverflow的提问,提问者说,为何他的test如果不加上timeout的限制,就永远不会停止,而如果加上了则会有exception发生。
StepVerifier.withVirtualTime(() ->
Flux.just(1, 2, 3, 4).delayElements(Duration.ofSeconds(1)))
.expectSubscription()
.expectNextCount(4)
.expectComplete()
.verify(Duration.ofSeconds(10));
//java.lang.AssertionError: VerifySubscribertimed out on reactor.core.publisher.FluxConcatMap$ConcatMapImmediate@66d1af89
如果你有认真阅读上面分享的内容并且吸收,你应该能看出问题点,为了避免暴雷,中间闲聊一下让大家思考,我看完找出问题後往下确认,发现回答的人居然是Simon Baslé!也就是Project Reactor的头头,这是不是有点像是你问了一个万有引力的问题,结果发现回答的人是牛顿。
公布答案!答案就是他完全没有用到任何会增加时间的方法,顺利成为了让时间停滞的男/女人,在上面的内容有提到如何增加时间,往上找一个时钟的图案就能发现了,最後附上Project Reactor的头头的精采回答
You need to use .thenAwait(Duration), otherwise the (virtual) clock won't move, and the delay never happens. You can also use .expectNoEvent(Duration) after the expectSubscription().
For example:
@Test
public void test() {
StepVerifier.withVirtualTime(() ->
Flux.just(1, 2, 3, 4).delayElements(Duration.ofSeconds(1)))
.expectSubscription() //t == 0
//move the clock forward by 1s, and check nothing is emitted in the meantime
.expectNoEvent(Duration.ofSeconds(1))
//so this effectively verifies the first value is delayed by 1s:
.expectNext(1)
//and so on...
.expectNoEvent(Duration.ofSeconds(1))
.expectNext(2)
//or move the clock forward by 2s, allowing events to take place,
//and check last 2 values where delayed
.thenAwait(Duration.ofSeconds(2))
.expectNext(3, 4)
.expectComplete()
//trigger the verification and check that in realtime it ran in under 200ms
.verify(Duration.ofMillis(200));
}
<<: [Day18] Flutter with GetX binding (二 ) 元件与属性绑定
BLoC BLoC全称为 Business Logic Component,表示为业务逻辑组件。是独...
贪吃蛇 教学原文参考:贪吃蛇 这篇文章会介绍如何使用「阵列」、「函式」、「变数」、「点亮」、「[计次...
今天要来设计一种算法来查找从一个人到另一个人的病毒链,可以算是复习前面的for回圈,及swap的应用...
创建App一关於本App(TeenMate) 为何选择在关於TeenMate的这一项功能作一天日记,...
monotonic :单调,递增或递减 这是一个看起来很简单的资料结构, 拥有 O(N) time ...