升级至Spring Boot 3与Java 21后控制器测试用例执行失败,请求排查并修复
看起来问题出在Spring Boot 3对MockMvc异常解析的行为变化,以及你的测试断言逻辑没有跟上这个变化。我们一步步来分析和修复:
问题根源
在Spring Boot 2中,MockMvc的result.getResolvedException()通常会返回最外层的ServletException,而实际业务异常(你的CustomException)是它的cause。但在Spring Boot 3的MockMvc实现中,这个行为发生了改变——getResolvedException()现在会直接返回最内层的业务异常(也就是你抛出的CustomException),而不是被Servlet容器包装后的外层异常。
你的测试代码还在尝试获取ex.getCause()来断言CustomException,但此时ex本身就是CustomException,它的cause其实是AnotherException(也就是你在catch块中传入的异常),这就导致断言失败。
修复方案(仅修改测试用例,无需改动业务逻辑)
调整测试中的断言逻辑,直接对getResolvedException()返回的异常进行类型断言即可,同时修正一处语法错误:
@Test public void testData() throws Exception { MyRequest mockRequest = mock(MyRequest.class); when(myService.getRequest()).thenReturn(mockRequest); when(myService.getFill(mockRequest)).thenThrow(new AnotherException("Test exception")); mockMvc.perform(post("/api{market}/{language}", "invalidMarket", "invalidLanguage")) // 补上缺失的闭合括号 .andExpect(result -> { Throwable ex = result.getResolvedException(); assertNotNull(ex); // 直接断言返回的异常就是CustomException assertInstanceOf(CustomException.class, ex); // 可选:如果需要验证异常的触发根源是AnotherException,可添加以下断言 // assertNotNull(ex.getCause()); // assertInstanceOf(AnotherException.class, ex.getCause()); }); }
额外验证小技巧
如果你想直观确认异常链的结构,可以在断言逻辑中临时添加打印语句:
// 临时添加,验证后可删除 System.out.println("解析到的异常类型:" + ex.getClass().getName()); if (ex.getCause() != null) { System.out.println("异常的根源类型:" + ex.getCause().getClass().getName()); }
运行测试后就能清晰看到Spring Boot 3中异常层级的变化。
关于异常解析器的补充说明
你在ControllerTestHelper中手动创建的ExceptionHandlerExceptionResolver在Spring Boot 3中依然可以工作,但要注意:这个解析器主要用于处理带有@ExceptionHandler注解的异常处理方法,如果你的控制器或全局异常处理器中没有定义这类方法,它不会改变MockMvc对未处理异常的返回行为。如果后续需要统一异常处理逻辑,推荐使用@RestControllerAdvice来实现,这样测试中的异常断言会更稳定。
备注:内容来源于stack exchange,提问作者karvai

