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

如何测试返回Mono<ResponseEntity<Void>>的Controller方法?

WebFlux Controller方法测试方案

针对你这个返回Mono<ResponseEntity<Void>>的接口,直接用Spring官方提供的WebTestClient测试即可,这是WebFlux栈专属的测试工具,原生支持响应式断言,不需要额外引入第三方依赖,常用的测试模式分两种:

单元测试(不启动完整Spring容器,执行速度最快)

这种模式只加载Controller本身,依赖的Service、Mapper全部用Mock对象替换,启动开销极小,适合做接口逻辑快速验证。
核心是把WebTestClient直接绑定到待测试的Controller实例上,不需要启动真实的Web服务器。
示例代码:

@ExtendWith(MockitoExtension.class)
class YourControllerTest {

    private YourController controller;
    @Mock
    private YourService targetService;
    private WebTestClient testClient;

    @BeforeEach
    void init() {
        // 手动初始化Controller,注入Mock的Service依赖
        controller = new YourController(targetService);
        // 绑定测试客户端到当前Controller实例
        testClient = WebTestClient.bindToController(controller).build();
    }

    @Test
    void shouldReturn200WhenCallFunctionName() {
        // 构造测试入参
        Request testReq = new Request();
        // 自行填充testReq的业务字段,比如 testReq.setBizId(1001L);
        // 给Service方法打桩,定义Mock调用逻辑
        doNothing().when(targetService).functionName(any(RequestDto.class));

        // 发起模拟请求、执行结果断言
        testClient.post() // 替换成你接口实际的HTTP方法,@GetMapping就换成get()
                .uri("/your/actual/api/path") // 替换成你接口实际配置的访问路径
                .bodyValue(testReq)
                .exchange()
                // 断言返回状态码为200 OK
                .expectStatus().isOk()
                // 断言返回体为空
                .expectBody(Void.class);

        // 验证Service层方法被正确调用,且入参Dto符合映射规则
        ArgumentCaptor<RequestDto> dtoCaptor = ArgumentCaptor.forClass(RequestDto.class);
        verify(targetService, times(1)).functionName(dtoCaptor.capture());
        RequestDto capturedParam = dtoCaptor.getValue();
        // 自行添加字段映射断言,比如 assertEquals(testReq.getBizId(), capturedParam.getBizId());
    }
}

集成测试(启动完整Spring上下文,适合全链路验证)

如果需要验证参数序列化、拦截器逻辑、全局异常处理等完整链路逻辑,可以启动完整Spring容器,用随机端口启动服务,直接注入WebTestClient发起请求即可:

// 随机端口启动服务,避免本地端口冲突
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class YourControllerIntegratedTest {

    @Autowired
    private WebTestClient testClient;
    // 用MockBean替换容器中真实的Service实例,避免真的调用下游依赖
    @MockBean
    private YourService targetService;

    @Test
    void shouldReturn200WhenCallFunctionName() {
        Request testReq = new Request();
        // 填充测试入参字段
        doNothing().when(targetService).functionName(any(RequestDto.class));

        testClient.post()
                .uri("/your/actual/api/path")
                .bodyValue(testReq)
                .exchange()
                .expectStatus().isOk()
                .expectBody(Void.class);

        verify(targetService, times(1)).functionName(any(RequestDto.class));
    }
}

注意:你贴的Controller代码存在响应式写法隐患:如果service.functionName(Dto)返回的是Mono/Flux类型的响应式对象,你现在的写法没有把它加入响应式执行链路,会出现响应式上下文丢失、方法不执行、线程切换异常的问题。正确写法应该把Service调用放到响应式操作符链路里,参考:

public Mono<ResponseEntity<Void>> functionName(final Request request) {
    RequestDto dto = RequestMapper.INSTANCE.toDto(request);
    return service.functionName(dto)
            .thenReturn(new ResponseEntity<>(HttpStatus.OK));
}

如果你的Service方法是普通同步阻塞方法,建议加上boundedElastic调度器调度,避免阻塞Netty的事件循环线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 23:06:28