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
相关产品推荐
相关产品推荐

