java - Spring Reactor 中的模拟服务

标签 java spring-boot mockito spring-webflux reactor

让我们看一下这个简单的方法:

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"))); 

一些运营商接受SupplierFunction它为这种急切的构造风格提供了一种懒惰的替代方案。一种通用的方法是使用 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.errorPublisherProbe 。这可以用来模拟 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/

相关文章:

java - 为什么java注解语法有()[括号]?

java - Spring Boot - 非网络应用程序的长期运行应用程序

java - 在 web 应用上下文中导入时,在 jar 文件中配置的 PersistenceUnit 具有错误的 PersistenceUnitRootLocation URL

java - 如何验证以 Lambda 作为参数的 ThreadPool 执行方法

java - 当 powermockito 中的 new() 调用不起作用时

java - 如何为 jgroup 复制 HashMap 中的状态传输设置超时?

java - 如何获取当前时间并将其设置为时钟?

java - 在 Spring Boot 2.0.4 中从 http 重定向到 https

android - 使用 Matchers.anyObject() 验证挂起函数

java - 使用 ArgumentMatcher 时出现 NullPointerException