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

如何为Spring WebFlux Router编写无Autowired Handler的JUnit单元测试

测试Spring WebFlux路由函数(非集成测试)

要测试这类RouterFunction端点,核心思路是手动Mock依赖的Handler、构建路由函数,再用WebTestClient发起请求验证,完全不需要依赖Spring上下文或Autowired注入。具体实现如下:

核心步骤

  • Mock EntityHandler:用Mockito创建EntityHandler的模拟实例,避免依赖真实业务实现。
  • 构建路由函数:直接实例化EntityRouter,调用其route方法传入Mock的Handler,得到待测试的路由规则。
  • 绑定WebTestClient:将构建好的路由函数绑定到WebTestClient,用它模拟HTTP请求,验证路由匹配、Handler调用及响应结果。

完整测试代码示例

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class EntityRouterTest {

    // 1. Mock依赖的EntityHandler
    private final EntityHandler mockHandler = Mockito.mock(EntityHandler.class);
    // 2. 手动构建路由函数
    private final RouterFunction<?> router = new EntityRouter().route(mockHandler);
    // 3. 绑定路由到WebTestClient,用于模拟请求
    private final WebTestClient webTestClient = WebTestClient.bindToRouterFunction(router).build();

    @Test
    void getAllEntities_ShouldTriggerFindAll() {
        // 预设Mock Handler的返回结果
        when(mockHandler.findAll(any())).thenReturn(Mono.just(ServerResponse.ok().build()));

        // 模拟GET请求
        webTestClient.get()
                .uri("/getAllEntities")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .expectStatus().isOk();

        // 验证Handler的findAll方法被正确调用
        verify(mockHandler).findAll(any());
    }

    @Test
    void getEntityById_ShouldTriggerFindById() {
        String testId = "1001";
        when(mockHandler.findById(any())).thenReturn(Mono.just(ServerResponse.ok().build()));

        webTestClient.get()
                .uri("/getEntity/{id}", testId)
                .accept(MediaType.APPLICATION_STREAM_JSON)
                .exchange()
                .expectStatus().isOk();

        verify(mockHandler).findById(any());
    }

    @Test
    void createEntity_ShouldTriggerSave() {
        // 假设存在Entity实体类
        Entity testEntity = new Entity("测试实体");
        when(mockHandler.save(any())).thenReturn(Mono.just(ServerResponse.ok().build()));

        webTestClient.post()
                .uri("/createEntity")
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(testEntity)
                .exchange()
                .expectStatus().isOk();

        verify(mockHandler).save(any());
    }

    @Test
    void deleteEntity_ShouldTriggerDelete() {
        String testId = "1001";
        when(mockHandler.delete(any())).thenReturn(Mono.just(ServerResponse.ok().build()));

        webTestClient.delete()
                .uri("/deleteEntity/{id}", testId)
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .expectStatus().isOk();

        verify(mockHandler).delete(any());
    }
}

关键细节说明

  • 整个测试是纯单元测试,完全脱离Spring容器,启动速度极快。
  • 可以通过WebTestClient验证路由的匹配规则(比如请求方法、路径、媒体类型是否符合预期)。
  • 利用Mockito的verify方法,确保路由与Handler的绑定逻辑正确,避免出现路由指向错误方法的问题。
  • 如果需要验证响应体内容,可在exchange()后添加.expectBody(...)方法,例如expectBodyList(Entity.class).hasSize(2)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 01:50:34