Spring Boot用MockMvc测试@Valid校验失败响应返回空body如何解决
问题原因
你遇到的响应为空的核心原因是:@WebMvcTest注解默认仅扫描指定的Controller类,不会加载你项目中定义的@RestControllerAdvice/@ControllerAdvice全局异常处理器。
Postman请求能拿到错误JSON是因为服务完整启动时,全局异常处理器会捕获校验抛出的BindException(@ModelAttribute参数校验失败抛出的是该异常,和@RequestBody抛出的MethodArgumentNotValidException不同),将其转换为JSON格式响应。单元测试时缺少异常处理器,异常未被转换为响应体,因此返回空字符串。
解决方案
方法1:导入全局异常处理器(推荐)
在测试类上添加@Import注解,导入你项目中的全局异常处理类即可,和生产环境行为完全一致:
@WebMvcTest(ActivationController.class) @Import(GlobalExceptionHandler.class) // 替换为你自己的全局异常处理类名 class ActivationControllerTest { @Autowired private MockMvc mockMvc; @Test void faulty_personal_information_responded_with_400_bad_request() throws Exception { mockMvc.perform( post("/activation/personal") .param("firstName", "BZ") .param("lastName", "") .accept(org.springframework.http.MediaType.APPLICATION_JSON) ) // 直接断言响应状态和内容,不需要手动拿Response .andExpect(status().isBadRequest()) // 按你实际返回的JSON结构断言,示例为错误信息在errors字段下,key为字段名 .andExpect(jsonPath("$.errors.lastName").value("姓氏不能为空")) .andDo(print()); // 打印完整请求响应日志到控制台,方便排查 } }
如果你的全局异常处理器还没有处理BindException,需要补充对应处理逻辑,示例:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BindException.class) public ResponseEntity<Map<String, String>> handleBindException(BindException e) { Map<String, String> errors = new HashMap<>(); e.getBindingResult().getFieldErrors().forEach(error -> errors.put(error.getField(), error.getDefaultMessage()) ); return ResponseEntity.badRequest().body(Collections.singletonMap("errors", errors)); } }
方法2:使用完整上下文测试
如果不想单独导入异常处理类,可以用@SpringBootTest替换@WebMvcTest,加载完整的Spring上下文,和生产环境加载的组件完全一致,缺点是测试启动速度较慢:
@SpringBootTest @AutoConfigureMockMvc class ActivationControllerTest { // 其余测试代码不变 }
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

