Spring Boot测试中将外部服务请求重定向到自定义工具类方案咨询
解决方案
以下两种方案均支持调用自定义工具类根据入参动态生成响应,可根据你的场景选择:
方案1:替换WebClient的ExchangeFunction(轻量无额外依赖,推荐)
WebClient的所有请求最终都会交给ExchangeFunction处理,你可以自定义该拦截器,匹配目标地址的请求直接转发到你的自定义工具类生成响应:
- 首先调整业务代码,将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的自定义转换器实现动态响应,并非只能返回静态内容:
- 引入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
相关产品推荐
相关产品推荐

