You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebFlux Mono.zip场景单测异常及高覆盖率编写方案咨询

问题根因

导致你代码不执行的核心问题如下:

  • 测试逻辑混用@SpringBootTest和@InjectMocks注解:@SpringBootTest会启动Spring上下文并自行实例化Bean,覆盖Mockito生成的代理对象,导致你预设的Mock返回值不生效,上游4个Mono没有正常发出值,自然不会触发Mono.zip和后续flatMap的执行逻辑
  • 测试类与被测类同名,引发类加载冲突,导致依赖注入异常
  • 原有业务代码存在两处错误:flatmap没有使用驼峰命名(正确写法为flatMap)、flatMap内部直接传入Mono对象作为DataResponse构造参数,没有从Mono.zip返回的Tuple4中取实际返回值

最佳单元测试实现方案

第一步:修正业务代码

@Service
public class TestService {
    @Autowired
    TestService1 testService1;
    @Autowired
    TestService2 testService2;
    @Autowired
    TestService3 testService3;
    @Autowired
    TestService4 testService4;

    public Mono<DataResponse> getData() {
        Mono<String> ts1 = testService1.getMono();
        Mono<String> ts2 = testService2.getMono();
        Mono<String> ts3 = testService3.getMono();
        Mono<String> ts4 = testService4.getMono();
        
        return Mono.zip(ts1, ts2, ts3, ts4)
        // 修正为驼峰命名flatMap
        .flatMap(resp -> {
             // line XX 从Tuple4中取出实际返回值构造响应
             return Mono.just(new DataResponse(resp.getT1(), resp.getT2(), resp.getT3(), resp.getT4()));
        });
    }
}

第二步:编写纯单元测试(无需启动Spring上下文)

使用Mockito+Reactor StepVerifier即可完成全链路测试,无需启动Spring容器,执行效率更高,避免上下文干扰。

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.Mockito.*;

// 仅使用Mockito扩展,不要加@SpringBootTest
@ExtendWith(MockitoExtension.class)
// 测试类名不要与被测类重复
public class TestServiceTest {
    @InjectMocks
    TestService testService;

    @Mock
    TestService1 testService1;
    @Mock
    TestService2 testService2;
    @Mock
    TestService3 testService3;
    @Mock
    TestService4 testService4;

    /**
     * 测试正常返回场景,覆盖flatMap内部逻辑
     */
    @Test
    void getData_shouldReturnCompleteResponseWhenAllServiceSuccess() {
        // 预设Mock返回的测试值
        String res1 = "service1Res";
        String res2 = "service2Res";
        String res3 = "service3Res";
        String res4 = "service4Res";
        when(testService1.getMono()).thenReturn(Mono.just(res1));
        when(testService2.getMono()).thenReturn(Mono.just(res2));
        when(testService3.getMono()).thenReturn(Mono.just(res3));
        when(testService4.getMono()).thenReturn(Mono.just(res4));

        Mono<DataResponse> resultMono = testService.getData();

        // StepVerifier校验全链路执行结果
        StepVerifier.create(resultMono)
                .expectNextMatches(response -> 
                    response != null
                    && res1.equals(response.getField1())
                    && res2.equals(response.getField2())
                    && res3.equals(response.getField3())
                    && res4.equals(response.getField4())
                )
                .verifyComplete();
        
        // 校验Mock方法确实被调用,避免逻辑遗漏
        verify(testService1, times(1)).getMono();
        verify(testService2, times(1)).getMono();
        verify(testService3, times(1)).getMono();
        verify(testService4, times(1)).getMono();
    }

    /**
     * 测试异常场景,覆盖错误处理链路(如果业务有自定义异常处理可以调整校验逻辑)
     */
    @Test
    void getData_shouldThrowErrorWhenAnyServiceFail() {
        // 模拟其中一个服务调用失败
        when(testService1.getMono()).thenReturn(Mono.error(new RuntimeException("call service1 failed")));
        when(testService2.getMono()).thenReturn(Mono.just("res2"));
        when(testService3.getMono()).thenReturn(Mono.just("res3"));
        when(testService4.getMono()).thenReturn(Mono.just("res4"));

        StepVerifier.create(testService.getData())
                .expectError(RuntimeException.class)
                .verify();
    }
}

覆盖率说明

以上两个测试用例可以覆盖getData方法的所有分支:正常返回场景覆盖flatMap内部全部代码,异常场景覆盖Mono.zip的错误传播链路,配合Mockito的调用校验可以确保所有依赖都被正确调用,实现100%代码覆盖率。

内容的提问来源于stack exchange,提问作者Shell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 04:45:02