
mockmvc 单元测试中,controller 抛出的自定义异常(如 resourcenotfoundexception)未被 @controlleradvice 拦截,导致测试直接失败而非返回预期的 404 响应。根本原因是 standalonesetup 默认不加载全局异常处理器,需显式注册。
mockmvc 单元测试中,controller 抛出的自定义异常(如 resourcenotfoundexception)未被 @controlleradvice 拦截,导致测试直接失败而非返回预期的 404 响应。根本原因是 standalonesetup 默认不加载全局异常处理器,需显式注册。
在使用 @WebMvcTest 进行控制器层测试时,若采用 MockMvcBuilders.standaloneSetup() 构建 MockMvc 实例(如当前测试中的 setUp() 方法),Spring 不会自动扫描并注册 @ControllerAdvice 类——即使该类已存在且正确配置。这导致 Controller 中抛出的 ResourceNotFoundException 无法被 AppExceptionHandler 拦截,进而以未处理异常(NestedServletException)形式终止请求,使 andExpect(status().isNotFound()) 断言失败。
✅ 正确做法:显式注册异常处理器
将 setUp() 方法修改为显式传入 AppExceptionHandler 实例:
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(underTest)
.setControllerAdvice(new AppExceptionHandler()) // ? 关键:手动注入异常处理器
.build();
}⚠️ 注意:setControllerAdvice(...) 接收的是实例对象(如 new AppExceptionHandler()),而非类类型;若异常处理器依赖 Spring Bean(如日志、服务等),建议通过 @Autowired 注入后传递,或改用 @WebMvcTest 的完整上下文模式(见下文备选方案)。
? 验证修复效果
修复后,原测试方法可正常通过:
@Test
void findAppUserById_throwsNotFound() throws Exception {
UUID uuid = UUID.fromString("ac0a1859-8e59-4ede-8def-043c32aa3208");
// 模拟服务层抛出异常
when(userDetailsService.findAppUserById(any())).thenThrow(
new ResourceNotFoundException("Kullanıcı bulunamadı")
);
MvcResult result = mockMvc
.perform(get("/app-users/" + uuid))
.andExpect(status().isNotFound()) // ✅ 现在能正确匹配 404
.andReturn();
String json = result.getResponse().getContentAsString();
ResourceNotFoundException ex = objectMapper.readValue(json, ResourceNotFoundException.class);
assertNotNull(ex);
assertEquals(404, ex.getHttpStatusCode());
assertEquals("Kullanıcı bulunamadı", ex.getMessage());
}? 补充说明与最佳实践
-
避免 @WebMvcTest(useDefaultFilters = false) 与 standaloneSetup 混用:当前测试同时使用了 @WebMvcTest 注解和手动 standaloneSetup,属于冗余配置。推荐二选一:
- ✅ 轻量级推荐:移除 @WebMvcTest,仅用 @ExtendWith(MockitoExtension.class) + standaloneSetup(需手动注册 Advice、Converter、MessageConverter 等);
- ✅ 全功能推荐:保留 @WebMvcTest,删除 standaloneSetup,改用 @Autowired MockMvc mockMvc —— 此时 Spring 会自动加载 @ControllerAdvice、@JsonComponent、WebMvcConfigurer 等,更贴近真实运行环境。
异常类序列化注意事项:ApplicationException 使用了 @JsonIgnoreProperties 忽略 stackTrace 等字段,确保 JSON 响应简洁安全;但需确认 ObjectMapper 在测试中未被额外配置覆盖(如未启用 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 可能影响 ZonedDateTime 序列化)。
Mockito 匹配建议:any() 匹配器应尽量替换为更精确的参数(如 eq(uuid)),提升测试可读性与健壮性。
通过显式注册 @ControllerAdvice,MockMvc 即可完整复现 Web 层异常处理链路,使控制器异常测试真正具备端到端语义验证能力。

















