Spring Boot中Spring WebClient调用外部REST API的集成测试最佳方案
测试依赖外部REST接口的Spring Boot应用:WireMock实践
是的,WireMock是处理这类场景的最优解——当你的应用依赖外部REST接口时,必须通过Stub模拟外部服务的响应,才能保证测试不依赖外部服务的可用性,同时精准覆盖各种业务分支(成功、失败、超时等)。
一、添加依赖
使用Spring Boot官方的WireMock Starter,集成更便捷:
Maven
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-contract-stub-runner</artifactId> <scope>test</scope> </dependency> <!-- 或单独使用WireMock核心依赖 --> <dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock-jre8</artifactId> <scope>test</scope> </dependency>
Gradle
testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner' // 或单独依赖 testImplementation 'com.github.tomakehurst:wiremock-jre8'
二、测试服务层(WebClient调用逻辑)
假设你有一个服务类,负责通过WebClient调用外部接口:
示例服务类
@Service public class ExternalApiService { private final WebClient webClient; public ExternalApiService(WebClient.Builder webClientBuilder) { this.webClient = webClientBuilder.baseUrl("https://external-api.com/api").build(); } public Mono<String> fetchExternalData(String userId) { return webClient.get() .uri("/users/{id}", userId) .retrieve() .bodyToMono(String.class); } }
服务层测试(用WireMock模拟外部接口)
import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.web.reactive.function.client.WebClient; import reactor.test.StepVerifier; import static com.github.tomakehurst.wiremock.client.WireMock.*; public class ExternalApiServiceTest { private static WireMockServer wireMockServer; private ExternalApiService service; @BeforeAll static void setup() { wireMockServer = new WireMockServer(8081); // 指定端口启动WireMock wireMockServer.start(); configureFor("localhost", 8081); } @BeforeEach void initService() { WebClient webClient = WebClient.builder() .baseUrl(wireMockServer.baseUrl()) .build(); service = new ExternalApiService(() -> webClient); } @AfterAll static void teardown() { wireMockServer.stop(); } @Test void fetchExternalData_shouldReturnSuccessResponse() { // Stub外部接口的成功响应 stubFor(get(urlEqualTo("/users/123")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"id\":\"123\",\"name\":\"John Doe\"}"))); // 调用服务方法并验证结果 StepVerifier.create(service.fetchExternalData("123")) .expectNext("{\"id\":\"123\",\"name\":\"John Doe\"}") .verifyComplete(); // 验证外部接口被正确调用 verify(getRequestedFor(urlEqualTo("/users/123"))); } @Test void fetchExternalData_shouldHandle404Error() { // Stub外部接口的404响应 stubFor(get(urlEqualTo("/users/999")) .willReturn(aResponse().withStatus(404))); StepVerifier.create(service.fetchExternalData("999")) .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException.NotFound) .verify(); } }
三、测试控制器层(端到端请求链路)
假设你有一个RestController,调用上述服务类:
示例控制器类
@RestController @RequestMapping("/api") public class UserController { private final ExternalApiService externalApiService; public UserController(ExternalApiService externalApiService) { this.externalApiService = externalApiService; } @GetMapping("/users/{userId}") public Mono<String> getUserData(@PathVariable String userId) { return externalApiService.fetchExternalData(userId); } }
控制器层集成测试(用@AutoConfigureWireMock自动配置)
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; import org.springframework.test.web.servlet.MockMvc; import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest(properties = {"external.api.base-url=http://localhost:8080/mock-api"}) @AutoConfigureMockMvc @AutoConfigureWireMock(port = 8080) // 自动启动WireMock并绑定端口 public class UserControllerTest { @Autowired private MockMvc mockMvc; @Test void getUserData_shouldReturnExternalData() throws Exception { // Stub外部接口的响应 stubFor(get(urlEqualTo("/mock-api/users/123")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"id\":\"123\",\"name\":\"John Doe\"}"))); // 调用本地控制器端点并验证响应 mockMvc.perform(get("/api/users/123")) .andExpect(status().isOk()) .andExpect(content().json("{\"id\":\"123\",\"name\":\"John Doe\"}")); // 验证WireMock收到了预期请求 verify(getRequestedFor(urlEqualTo("/mock-api/users/123"))); } }
关键优势总结
- 隔离依赖:完全脱离外部服务的状态,测试稳定可靠
- 场景覆盖:轻松模拟各种HTTP状态码、响应体、超时等场景,覆盖业务异常分支
- 精准验证:可以验证外部接口的调用次数、参数是否符合预期
内容的提问来源于stack exchange,提问作者Spartacus
相关产品推荐
相关产品推荐

