SpringBoot应用中如何Mock外部URL响应以测试API全流程?
测试SpringBoot完整流程并Mock外部REST请求
针对你这种Controller→Service→ClientClass的调用链,且外部URL不可用的场景,有两种常用的Mock方案,都能覆盖从Controller开始的完整流程:
方案一:用Mockito直接Mock RestTemplate
这种方式适合快速Mock单个外部请求,无需启动额外服务。
步骤:
确保测试依赖齐全
你的pom.xml(Maven)或build.gradle(Gradle)里要包含Spring Boot测试 starter,它自带Mockito:<!-- Maven --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>编写测试类
使用@SpringBootTest加载完整应用上下文,用@MockBean替换真实的RestTemplate,然后指定Mock的响应逻辑:import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.context.ApplicationContext; @SpringBootTest public class FullFlowTest { @Autowired private ApplicationContext context; private MockMvc mockMvc; @MockBean private RestTemplate restTemplate; @Test public void testFullFlow() throws Exception { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); // 定义Mock的外部响应内容 String mockExternalResponse = "{\"code\":200,\"msg\":\"success\"}"; when(restTemplate.postForEntity( eq("http://external-url.com/api"), // 匹配目标URL any(Object.class), // 匹配任意请求体 eq(String.class) // 匹配响应类型 )).thenReturn(new ResponseEntity<>(mockExternalResponse, HttpStatus.OK)); // 发起对Controller的请求,验证完整流程 mockMvc.perform(post("/your-controller-endpoint") .contentType("application/json") .content("{\"requestData\":\"test\"}")) .andExpect(status().isOk()); } }
方案二:用WireMock模拟完整外部服务
如果需要模拟更复杂的外部服务行为(比如不同请求返回不同响应、模拟异常),WireMock是更好的选择。
步骤:
添加WireMock依赖
<!-- Maven --> <dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock-jre8</artifactId> <scope>test</scope> <version>2.35.0</version> </dependency>编写测试类
启动WireMock服务器,配置Stub规则,然后让ClientClass指向WireMock的地址:import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.context.ApplicationContext; import static com.github.tomakehurst.wiremock.client.WireMock.*; @SpringBootTest(properties = {"external.api.url=http://localhost:8089/api"}) public class WireMockFullFlowTest { @Autowired private ApplicationContext context; private MockMvc mockMvc; private WireMockServer wireMockServer; @BeforeEach void setup() { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); // 启动WireMock服务器,监听8089端口 wireMockServer = new WireMockServer(8089); wireMockServer.start(); configureFor("localhost", 8089); // 配置Stub:匹配POST请求,返回指定响应 stubFor(post(urlEqualTo("/api")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"code\":200,\"msg\":\"success\"}"))); } @Test public void testFullFlowWithWireMock() throws Exception { // 发起对Controller的请求,验证完整流程 mockMvc.perform(post("/your-controller-endpoint") .contentType("application/json") .content("{\"requestData\":\"test\"}")) .andExpect(status().isOk()); } @AfterEach void teardown() { wireMockServer.stop(); } }
注意事项
- 如果你的ClientClass是通过构造函数注入
RestTemplate,Mock时要确保注入的是Mock实例 - 用WireMock时,要把ClientClass里的外部URL配置为WireMock的地址(可以通过测试环境变量覆盖)
- 测试时要验证不仅是状态码,还要验证业务逻辑的输出是否符合预期
内容的提问来源于stack exchange,提问作者Aslam P Sherif
相关产品推荐
相关产品推荐

