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

Spring Boot测试中将外部服务请求重定向到自定义工具类方案咨询

解决方案

以下两种方案均支持调用自定义工具类根据入参动态生成响应,可根据你的场景选择:


方案1:替换WebClient的ExchangeFunction(轻量无额外依赖,推荐)

WebClient的所有请求最终都会交给ExchangeFunction处理,你可以自定义该拦截器,匹配目标地址的请求直接转发到你的自定义工具类生成响应:

  1. 首先调整业务代码,将WebClient实例交给Spring容器管理,避免硬编码初始化:
@Configuration
public class WebClientConfig {
    @Bean
    public WebClient externalServiceWebClient() {
        return WebClient.builder()
                .baseUrl("http://localhost:9600/external_host_path")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();
    }
}

业务代码中直接@Autowired注入该WebClient实例使用即可。
2. 测试类中替换WebClient的实现,加入自定义拦截逻辑:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyControllerTest {
    @Autowired
    MyController controller;
    MockMvc mockMvc;
    // 你已经实现的自定义响应生成工具类
    @Autowired
    CustomResponseGenerator customResponseGenerator;
    @TestConfiguration
    static class MockWebClientConfig {
        @Bean
        @Primary
        public WebClient mockExternalWebClient() {
            ExchangeFunction mockExchange = request -> {
                String targetUrlPrefix = "http://localhost:9600/external_host_path";
                // 匹配目标地址的请求
                if (request.url().toString().startsWith(targetUrlPrefix)) {
                    // 提取请求的方法、路径、body等参数
                    HttpMethod method = request.method();
                    String path = request.url().getPath().replace("/external_host_path", "");
                    String requestBody = request.body() != null ? request.body().toString() : "";
                    // 调用自定义工具类生成响应
                    Object mockResult = customResponseGenerator.generate(method, path, requestBody);
                    // 构造WebClient响应返回
                    return Mono.just(ClientResponse.create(HttpStatus.OK)
                            .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                            .bodyValue(mockResult)
                            .build());
                }
                // 非目标地址的请求可走正常逻辑,不需要的话直接抛出异常即可
                return ExchangeFunctions.create().exchange(request);
            };
            return WebClient.builder()
                    .baseUrl("http://localhost:9600/external_host_path")
                    .exchangeFunction(mockExchange)
                    .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                    .build();
        }
    }
    @Before
    public void setup() throws Exception {
        this.mockMvc = standaloneSetup(this.controller).build();
    }
    @Test
    public void testControl() throws Exception {
        mockMvc
        .perform(post("/my_path") // standalone模式无需填写全路径,直接写接口路径即可
        .contentType(MediaType.APPLICATION_JSON)
        .content("{\"someData\":\"[]\"}")) // JSON格式要求用双引号
        .andExpect(status().isAccepted())
        .andReturn();
    }
}

方案2:WireMock动态响应(适合需要真实HTTP交互的集成测试场景)

如果你不想修改业务代码的WebClient初始化逻辑,可以用WireMock的自定义转换器实现动态响应,并非只能返回静态内容:

  1. 引入WireMock测试依赖,测试类启动WireMock并绑定你要拦截的端口9600:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWireMock(port = 9600) // 直接占用目标端口,无需修改业务代码的请求地址
public class MyControllerTest {
    @Autowired
    MyController controller;
    MockMvc mockMvc;
    @Autowired
    CustomResponseGenerator customResponseGenerator;
    @Autowired
    ObjectMapper objectMapper;
    @Before
    public void setup() throws Exception {
        this.mockMvc = standaloneSetup(this.controller).build();
        // 配置拦截所有目标路径的请求
        stubFor(any(urlPathMatching("/external_host_path/.*"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        // 自定义响应生成逻辑
                        .withBody(objectMapper.writeValueAsString(
                                customResponseGenerator.generate(
                                        Request.getCurrent().getMethod().getName(),
                                        Request.getCurrent().getUrl(),
                                        Request.getCurrent().getBodyAsString()
                                )
                        ))));
    }
    @Test
    public void testControl() throws Exception {
        mockMvc
        .perform(post("/my_path")
        .contentType(MediaType.APPLICATION_JSON)
        .content("{\"someData\":\"[]\"}"))
        .andExpect(status().isAccepted())
        .andReturn();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 17:06:05