让我们看一下这个简单的方法:
public Mono<SuccessResponse> doSomething(){
return service1.doSomething()
.then(service2.doSomething2())
.thenReturn(new SuccessResponse("Awesome")));
}
所以基本上我想测试这个方法的场景,其中 service1.doSomething() 将抛出错误:
when(service1.doSomething()).thenReturn(Mono.error(new IllegalStateException("Something bad happened")));
when(service2.doSomething()).thenReturn(Mono.just(new SomeResponse()))
assertThatThrownBy(() -> testedService.doSomething().block())
.isExactlyInstanceOf(IllegalStateException.class);
verify(service2, never()).doSomething(); //Why this is executed!?
我的问题是为什么 service2.doSomething() 被执行一次?它不应该被执行,因为 service1.doSomething() 在上面抛出一个错误...
最佳答案
service2.doSomething()
的原因调用方法是当 Mono
可能是懒惰的,显然调用运算符(operator)不是。您急切地调用将返回惰性 Mono
的方法s,从而组装出一个处理管道。
如果您内联代码,我认为它会变得更清晰:
//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
隐藏一个非惰性源(例如,从 Mono
改编而来的 CompletableFuture
),问题不在于 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();
关于java - Spring Reactor 中的模拟服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54148272/