You need to enable JavaScript to run this app. Controller层测试问题排查:@WebMvcTest测试返回500而非200,Swagger调试正常
优惠活动
大模型
产品
解决方案
定价
更多

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。
  • 快速获取真实异常的操作
    • 现有测试代码只打印了响应内容,没有捕获请求处理过程中抛出的原始异常,根本看不到500的触发根源。只要在测试逻辑里加上两行代码,就能拿到完整的错误堆栈:
      MvcResult mvcResult = mockMvc.perform(post("/v1/user")
                      .contentType(MediaType.APPLICATION_JSON)
                      .content(content))
                          .andDo(MockMvcResultHandlers.print()) // 打印完整的请求、响应、异常日志
                          .andReturn();
      // 直接打印请求过程中抛出的原始根异常
      System.out.println("测试触发的原始异常:" + mvcResult.getResolvedException());
      
      拿到异常堆栈后,就能直接定位是组件缺失、空指针还是其他类型的问题。

内容的提问来源于stack exchange,提问作者ChoiTaesan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.10 23:36:03