
mockmvc 单元测试中,controller 抛出的自定义异常(如 resourcenotfoundexception)未被 @controlleradvice 拦截,导致测试直接抛出嵌套异常而非返回预期的 404 响应体,根本原因是 standalonesetup 未注册异常处理器。
mockmvc 单元测试中,controller 抛出的自定义异常(如 resourcenotfoundexception)未被 @controlleradvice 拦截,导致测试直接抛出嵌套异常而非返回预期的 404 响应体,根本原因是 standalonesetup 未注册异常处理器。
在使用 @WebMvcTest 进行控制器层测试时,若采用 MockMvcBuilders.standaloneSetup(...) 手动构建 MockMvc 实例(如当前测试中的 setUp() 方法),Spring 将不会自动加载通过 @ControllerAdvice 声明的全局异常处理器(如 AppExceptionHandler)。这会导致 Controller 中抛出的 ResourceNotFoundException 未经处理直接向上冒泡,最终触发 NestedServletException,使 @ResponseStatus 或自定义异常转换逻辑失效——测试断言 status().isNotFound() 会失败,且响应体为空或非预期 JSON,无法反序列化为 ResourceNotFoundException 对象。
✅ 正确做法是:在 standaloneSetup 中显式注册异常处理器类:
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(underTest)
.setControllerAdvice(new AppExceptionHandler()) // ? 关键:手动注入异常处理器
.build();
}
⚠️ 注意事项:
- @WebMvcTest(useDefaultFilters = false) 已禁用默认组件扫描,因此 @ControllerAdvice 类不会被自动发现,必须手动传入;
- setControllerAdvice(...) 支持传入多个实例(如 new AppExceptionHandler(), new ValidationExceptionHandler());
- 确保 AppExceptionHandler 的 @ExceptionHandler 方法签名与实际抛出异常类型严格匹配(此处为 ApplicationException 及其子类,已满足);
- 若使用 @WebMvcTest(AppUserController.class)(不设 useDefaultFilters = false),且 AppExceptionHandler 在扫描路径下,则可省略手动注册——但本例因禁用了默认过滤器,故必须显式添加。
? 补充建议:优化测试可读性与健壮性
- 避免 any() 与具体值混用:当前 when(userDetailsService.findAppUserById(any())).thenThrow(...) 会覆盖所有调用,建议改用 eq(uuid) 确保行为精准匹配测试场景;
- 验证响应内容结构:除 status().isNotFound() 外,可增加 .andExpect(content().contentType(MediaType.APPLICATION_JSON)) 防止 Content-Type 错误;
- 异常类需支持 JSON 序列化:确认 ResourceNotFoundException(及其父类 ApplicationException)的字段均有 public getter,且 @JsonIgnoreProperties 未意外屏蔽关键字段(如 httpStatusCode、message)。
修复后,测试将正确触发 AppExceptionHandler.handleApplicationException(),返回状态码 404 及包含 resultCode、message、httpStatusCode 等字段的 JSON 响应体,objectMapper.readValue(...) 可成功解析,断言通过。











