[英]Mock services in Spring Reactor
Let's take a look over this simple method: 让我们看一下这个简单的方法:
public Mono<SuccessResponse> doSomething(){
return service1.doSomething()
.then(service2.doSomething2())
.thenReturn(new SuccessResponse("Awesome")));
}
So basically I want to test this method for a scenario in which, service1.doSomething() will throw an error: 因此,基本上,我想针对其中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!?
My question is why service2.doSomething() is executed once? 我的问题是为什么service2.doSomething()被执行一次? it shouldn't be executed since service1.doSomething() throw an error above...
它不应该执行,因为service1.doSomething()在上面抛出错误...
The reason why the service2.doSomething()
method is invoked is that while a Mono
can be lazy, plainly calling an operator isn't. 调用
service2.doSomething()
方法的原因是,虽然Mono
可以是惰性的,但不能简单地调用运算符。 You are eagerly calling the methods that will return lazy Mono
s, thus assembling a processing pipeline. 您急切地调用将返回惰性
Mono
的方法,从而组装了处理管道。
If you inline your code, it becomes a bit clearer I think: 如果您内联代码,我认为它会变得更加清晰:
//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")));
Some operators accept Supplier
or Function
which provides a lazy alternative to this eager construction style. 一些操作员接受“
Supplier
或“ Function
,这是这种急切的构造样式的一种替代方案。 One universal way of doing that is to use Mono.defer
: 一种通用的方式是使用
Mono.defer
:
public Mono<SuccessResponse> doSomething(){
return service1.doSomething()
.then(Mono.defer(service2::doSomething2))
.thenReturn(new SuccessResponse("Awesome")));
}
But I'd argue that , unless service2
hides a source that is NOT lazy (eg. a Mono
adapted from a CompletableFuture
) , the problem is not the doSomething
but the test . 但是我认为 , 除非
service2
隐藏了一个不是惰性的源(例如,从CompletableFuture
改编的Mono
) , 否则问题不是doSomething
而是测试 。
With the service2
mock, you are essentially testing the assembly of the chain of operators, but not if that step in the pipeline is actually executed. 使用
service2
模拟,您实际上是在测试运算符链的组装,但是如果管道中的该步骤实际上已执行,则不会进行测试。
One trick available in reactor-test
is to wrap the Mono.just
/ Mono.error
in a PublisherProbe
. reactor-test
可用的一个技巧是将Mono.just
/ Mono.error
包装在PublisherProbe
。 This can be used to mock a Mono
like you did, but with the added feature of providing assertions on the execution of the Mono
: was it subscribed to? 它可以像您一样模拟
Mono
,但是具有在Mono
执行时提供断言的附加功能:它是否已订阅? was it requested? 有要求吗?
//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();
thenReturn
is not for throwing error ! thenReturn
不是因为抛出错误! You need to use thenThrow()
and also you don't need to write mock method for service2 , just do verify which have called 您需要使用
thenThrow()
并且也不需要为service2编写模拟方法,只需验证已调用了
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.