Controller层测试问题排查:@WebMvcTest测试返回500而非200,Swagger调试正常
问题场景
- 项目用户注册接口
POST /v1/user在实际运行时通过Swagger调试完全正常,可正确返回200状态码、完成注册逻辑 - 编写Controller层切片测试用例时,MockMvc运行测试却返回500状态码,抛出断言失败错误,与实际运行行为不一致
- 业务接口核心实现代码:
@RequiredArgsConstructor @RestController public class UserController { private final UserService userService; @PostMapping(value="/v1/user") public ResponseEntity<ResponseDto> postUser(@RequestBody UserSaveRequestDto userSaveRequestDto) { Long userId = userService.insertUser(userSaveRequestDto); return CommonUtil.getResponseEntity( UserResponseDto.builder().userId(userId).build(), HttpStatus.OK, "회원 등록 완료" ); } } - 测试用例核心实现代码:
@WebMvcTest public class UserControllerTest { @Autowired MockMvc mockMvc; @Autowired ObjectMapper objectMapper; @MockBean UserService userService; UserSaveRequestDto userSaveRequestDto; @BeforeEach void setUp() { userSaveRequestDto = UserSaveRequestDto.builder() .userName("test") .userPhoneNumber("01026137832") .build(); } @DisplayName("MockMvc를 이용한 postUser slice 테스트") @Test public void postUserTest() throws Exception { // given given(userService.insertUser(userSaveRequestDto)).willReturn(1L); String content = objectMapper.writeValueAsString(userSaveRequestDto); // when MvcResult mvcResult = mockMvc.perform(post("/v1/user") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andExpect(status().isOk()) .andReturn(); // then String result = mvcResult.getResponse().getContentAsString(); System.out.println(result); } }
500错误的核心排查方向
- 切片测试的上下文加载范围限制
@WebMvcTest是Spring Boot提供的轻量切片测试注解,默认只会加载和Spring MVC栈直接相关的组件(Controller、拦截器、消息转换器、全局异常处理器等),不会加载项目中自定义通用工具类所依赖的容器Bean- 你接口逻辑里调用的
CommonUtil.getResponseEntity方法,如果内部依赖了Spring容器中的组件(比如多语言消息解析器、统一响应结构的配置类),在切片测试环境下因为没有扫描加载这些Bean,方法执行时就会抛出空指针、组件缺失类异常,最终被Spring MVC捕获后返回500状态码。而实际生产环境启动了完整的Spring容器,所有CommonUtil依赖的Bean都会被正常加载,所以Swagger调用时不会出现异常,这也正好解释了“实际正常、测试报错”的现象。
- Mock规则匹配失效
- 你编写的Mock规则是严格对象匹配:
given(userService.insertUser(userSaveRequestDto)).willReturn(1L),只有当传入insertUser方法的参数和你测试类提前构造的userSaveRequestDto对象逻辑相等时,才会触发返回1L的逻辑 - 实际请求处理流程中,Spring会把传入的JSON字符串反序列化成一个全新的
UserSaveRequestDto对象,如果这个DTO类没有重写equals和hashCode方法,两个对象的引用地址不同,Mock规则就不会生效,insertUser方法会返回Mock默认的null值;后续构造响应时如果对userId有非空校验,就会直接抛出异常触发500。
- 你编写的Mock规则是严格对象匹配:
- 快速获取真实异常的操作
- 现有测试代码只打印了响应内容,没有捕获请求处理过程中抛出的原始异常,根本看不到500的触发根源。只要在测试逻辑里加上两行代码,就能拿到完整的错误堆栈:
拿到异常堆栈后,就能直接定位是组件缺失、空指针还是其他类型的问题。MvcResult mvcResult = mockMvc.perform(post("/v1/user") .contentType(MediaType.APPLICATION_JSON) .content(content)) .andDo(MockMvcResultHandlers.print()) // 打印完整的请求、响应、异常日志 .andReturn(); // 直接打印请求过程中抛出的原始根异常 System.out.println("测试触发的原始异常:" + mvcResult.getResolvedException());
- 现有测试代码只打印了响应内容,没有捕获请求处理过程中抛出的原始异常,根本看不到500的触发根源。只要在测试逻辑里加上两行代码,就能拿到完整的错误堆栈:
内容的提问来源于stack exchange,提问作者ChoiTaesan
相关产品推荐
相关产品推荐

