简体   繁体   English

Spring Reactor中的模拟服务

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM