调用service2.doSomething() 方法的原因是,虽然Mono 可以是惰性的,但显然调用运算符不是。您急切地调用将返回惰性Monos 的方法,从而组装处理管道。
如果你内联你的代码,我认为它会变得更清晰:
//exception is CREATED immediately, but USED lazily
return Mono.error(new IllegalStateException())
//mono is CREATED immediately. The data it will emit is also CREATED immediately. But it all triggers LAZILY.
.then(Mono.just(new SomeResponse()))
//note that then* operators completely ignore previous step's result (unless it is an error)
.thenReturn(new SuccessResponse("Awesome")));
一些运营商接受Supplier 或Function,这为这种急切的构造风格提供了一种懒惰的替代方案。一种通用的方法是使用Mono.defer:
public Mono<SuccessResponse> doSomething(){
return service1.doSomething()
.then(Mono.defer(service2::doSomething2))
.thenReturn(new SuccessResponse("Awesome")));
}
但我认为,除非service2 隐藏了一个不懒惰的来源(例如,改编自CompletableFuture 的Mono), 问题不是doSomething,而是测试。
使用service2 模拟,您实际上是在测试运算符链的组装,但如果管道中的该步骤实际执行,则不是。
reactor-test 中可用的一个技巧是将Mono.just/Mono.error 包装在PublisherProbe 中。这可以像你一样用来模拟Mono,但是增加了在Mono的执行上提供断言的附加功能:它订阅了吗?有要求吗?
//this is ultimately tested by the assertThrownBy, let's keep it that way:
when(service1.doSomething()).thenReturn(Mono.error(new IllegalStateException("Something bad happened")));
//this will be used to ensure the `service2` Mono is never actually used:
PublisherProbe<SomeResponse> service2Probe = PublisherProbe.of(Mono.just(new SomeResponse()));
//we still need the mock to return a Mono version of our probe
when(service2.doSomething()).thenReturn(service2Probe.mono());
assertThatThrownBy(() -> testedService.doSomething().block())
.isExactlyInstanceOf(IllegalStateException.class);
//service2 might have returned a lazy Mono, but it was never actually used:
probe.assertWasNotSubscribed();