如何使用Mockito测试Repository中的InternalServerError?控制器POST接口失败测试返回415而非预期500的问题排查
一、先解决415不支持媒体类型的问题
你遇到的第一个415错误,根源非常明确:你的失败测试请求没有指定Content-Type。
看你的成功测试代码,你明确添加了.contentType(MediaType.APPLICATION_JSON),但失败测试里只加了.accept(MediaType.APPLICATION_JSON)——accept是告诉服务器你接受什么类型的响应,而contentType才是告诉服务器你发送的请求体是什么类型。没有指定Content-Type的话,Spring无法解析你的JSON请求体,直接返回415错误。
修改失败测试的请求构建代码,补上contentType配置:
@Test @DisplayName("POST /customers - Failure") void createProductShouldFail() throws Exception { // Setup mocked service when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class); RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/customers") .contentType(MediaType.APPLICATION_JSON) // 新增这一行 .accept(MediaType.APPLICATION_JSON) .content("{\"name\":\"John\"}"); MvcResult result=mockMvc.perform(requestBuilder).andReturn(); MockHttpServletResponse response = result.getResponse(); // Validate the response code and content type assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus()); }
二、解决添加注解后测试抛出异常的问题
你添加@RestController和@EnableWebMvc后出现NestedServletException,原因是:
你的控制器try-catch只捕获了URISyntaxException,但Mock的服务抛出的HttpServerErrorException.InternalServerError并没有被捕获,这个异常会被Spring的Servlet容器包装成NestedServletException抛出,导致测试在mockMvc.perform时直接报错,而不是得到预期的500响应。
要让控制器返回500状态码,有两种可行方案:
方案1:扩展控制器的异常捕获范围
修改控制器的catch块,把HttpServerErrorException也纳入捕获范围(或者直接捕获Exception,但更推荐精准捕获特定异常):
@PostMapping(path = "/customers") public ResponseEntity<Customer> saveCustomer(@RequestBody Customer customer){ try { Customer savedCustomer = customerService.save(customer); return ResponseEntity .created(new URI("/customers/" + savedCustomer.getId())) .body(savedCustomer); } catch (URISyntaxException | HttpServerErrorException e) { // 添加HttpServerErrorException return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } }
方案2:利用Spring的默认异常处理器
如果你不想在控制器里手动捕获异常,Spring的默认异常处理器会自动把HttpServerErrorException这类Spring HTTP异常转换成对应的状态码响应。此时你可以把测试代码改成链式调用的方式,用andExpect直接断言状态码:
@Test @DisplayName("POST /customers - Failure") void createProductShouldFail() throws Exception { when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class); mockMvc.perform(MockMvcRequestBuilders.post("/customers") .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .content("{\"name\":\"John\"}")) .andExpect(status().isInternalServerError()); // 直接断言状态码 }
三、验证断言是否生效的方法
要确认你的状态码断言是否正常工作,可以通过以下方式验证:
- 在测试中添加
.andDo(print()),MockMvc会打印完整的请求和响应信息,你可以直观看到响应的状态码:mockMvc.perform(requestBuilder) .andDo(print()) .andExpect(status().isInternalServerError()); - 如果用原来的
andReturn()方式获取响应,可以打印response.getStatus()的值,直接确认实际返回的状态码:MockHttpServletResponse response = result.getResponse(); System.out.println("实际响应状态码:" + response.getStatus()); // 打印验证 assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
内容的提问来源于stack exchange,提问作者wick3d

