如何Mock子服务REST请求并使用MockMvc测试@RestController?
解决MockMvc测试REST控制器+Mock子服务请求的方案
看起来你已经搭好了MockMvc测试的基础架子,接下来咱们把这两个需求落地得更完善:
一、优化MockMvc测试@RestController
你的初始代码已经能跑,但可以用Spring提供的自动配置简化流程,同时增加更全面的断言:
优化后的测试代码
@RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc // 自动配置MockMvc,无需手动通过WebApplicationContext构建 public class WebControllerIT { @Autowired private MockMvc mockMvc; @Test public void getStatusReturnsValidResponse() throws Exception { mockMvc.perform(get("/status") .contentType(MediaType.APPLICATION_JSON)) // 指定请求类型,贴近真实调用场景 .andExpect(status().isOk()) // 断言响应状态码 .andExpect(content().contentType(MediaType.TEXT_PLAIN)) // 断言响应内容类型 .andExpect(content().string(containsString("OK"))); // 断言响应体包含预期内容 } }
关键优化点
@AutoConfigureMockMvc替代手动setup:Spring会自动注入配置好的MockMvc实例,省去手动构建的代码- 增加多维度断言:除了状态码,还可以验证响应内容类型、响应体内容,让测试更严谨
二、Mock子服务的REST请求
根据你调用子服务的方式,有两种常用方案:
方案1:用Spring自带的MockRestServiceServer(适合RestTemplate场景)
如果你的控制器是通过RestTemplate调用子服务,用MockRestServiceServer可以直接拦截RestTemplate的请求,无需启动额外服务:
假设你的业务代码是这样的:
@RestController public class WebController { private final RestTemplate restTemplate; // 构造注入RestTemplate public WebController(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @GetMapping("/status") public String getStatus() { // 调用子服务的健康检查接口 String subServiceHealth = restTemplate.getForObject("http://sub-service/api/health", String.class); return "Main Service OK | Sub Service: " + subServiceHealth; } }
对应的测试代码:
@RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class WebControllerIT { @Autowired private MockMvc mockMvc; @Autowired private RestTemplate restTemplate; private MockRestServiceServer mockServer; @Before public void setup() { // 将Mock服务绑定到RestTemplate,拦截所有请求 mockServer = MockRestServiceServer.createServer(restTemplate); } @Test public void getStatusReturnsOkWithMockedSubService() throws Exception { // 配置Mock规则:当请求子服务的/api/health时,返回"UP" mockServer.expect(requestTo("http://sub-service/api/health")) .andExpect(method(HttpMethod.GET)) // 验证请求方法 .andRespond(withSuccess("UP", MediaType.TEXT_PLAIN)); // 执行MockMvc请求 mockMvc.perform(get("/status") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(containsString("Main Service OK | Sub Service: UP"))); // 验证Mock的请求是否被正确触发 mockServer.verify(); } }
方案2:用WireMock(适合复杂HTTP场景)
如果需要模拟更复杂的子服务行为(比如POST请求、参数校验、错误状态码、响应延迟),WireMock是更灵活的选择,它是一个独立的HTTP Mock服务器:
第一步:添加依赖(Maven)
<dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock-jre8</artifactId> <version>2.35.0</version> <scope>test</scope> </dependency>
第二步:编写测试代码
@RunWith(SpringRunner.class) // 让RestTemplate指向WireMock的地址 @SpringBootTest(properties = {"sub-service.url=http://localhost:8089"}) public class WebControllerIT { @Autowired private MockMvc mockMvc; private WireMockServer wireMockServer; @Before public void setup() { // 启动WireMock服务器,监听8089端口 wireMockServer = new WireMockServer(8089); wireMockServer.start(); WireMock.configureFor("localhost", 8089); } @After public void teardown() { // 测试结束后停止服务器 wireMockServer.stop(); } @Test public void getStatusHandlesSubServiceUnavailable() throws Exception { // 配置WireMock:模拟子服务返回500错误 stubFor(get(urlEqualTo("/api/health")) .willReturn(aResponse() .withStatus(500) .withBody("ERROR"))); mockMvc.perform(get("/status") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(containsString("Main Service OK | Sub Service: ERROR"))); // 验证请求是否被正确调用 verify(getRequestedFor(urlEqualTo("/api/health"))); } }
方案选择建议
- 简单Mock
RestTemplate请求:用MockRestServiceServer足够,轻量且贴合Spring生态 - 复杂HTTP场景模拟:用WireMock,支持更丰富的请求/响应配置,适合模拟真实子服务的各种行为
内容的提问来源于stack exchange,提问作者sschmeck
相关产品推荐
相关产品推荐

