Kotlin开发中如何测试控制器抛出指定自定义异常?
MockMvc场景下校验自定义异常抛出的实现方案
针对你已有的测试基础,核心不需要改动原有接口请求逻辑,只需要在原有断言链上扩展对应校验逻辑即可,根据项目的异常处理配置不同,有两种可直接落地的写法:
1. 通用适配写法(适配绝大多数带全局异常处理器的项目)
不管你有没有配置全局@ControllerAdvice,都可以通过MockMvc内置的resolvedException属性直接拿到请求执行过程中抛出的真实异常对象,直接做类型校验,不需要依赖响应体的字段约定:
- 若测试类已引入AssertJ断言依赖可跳过依赖配置环节
- 在原有的断言链上追加异常类型断言即可,示例代码:
@Test fun `校验传入无效ISO编码时抛出InvalidIsoCodeException`() { mockMvc.perform(get("/countries/UA")) .andDo(print()) .andExpect(status().is4xxClientError) // 直接校验请求解析过程中捕获到的异常就是目标自定义类型 .andExpect { assertThat(it.resolvedException).isInstanceOf(InvalidIsoCodeException::class.java) } }
说明:
resolvedException是MockMvc在执行请求时,从Spring MVC的DispatcherServlet捕获到的全链路异常对象,哪怕后续被全局异常处理器转换成了标准错误响应,这个属性也会保留原始的异常实例,完全可以满足你校验自定义异常抛出的需求。
2. 符合全局响应规范的扩展校验
如果你们的项目约定全局异常处理器会把异常类型、错误信息写入统一的响应体结构(比如RESTful API的标准错误格式),还可以直接校验响应体内容来匹配生产环境的实际返回行为:
@Test fun `校验传入无效ISO编码时抛出InvalidIsoCodeException`() { mockMvc.perform(get("/countries/UA")) .andDo(print()) .andExpect(status().is4xxClientError) // 校验响应体中记录的异常标识和自定义异常匹配 .andExpect(jsonPath("$.errorType").value(InvalidIsoCodeException::class.java.simpleName)) // 可同步校验异常携带的具体业务提示 .andExpect(jsonPath("$.message").value("国家ISO编码不符合规则")) }
常见注意点
- 不需要为了测试刻意关闭全局异常处理器,否则测试验证的行为和线上运行逻辑会出现偏差
- 不要通过手动try-catch的方式捕获
MockMvc.perform抛出的异常,绝大多数场景下Spring MVC会把异常消化在请求链路内部,这种方式会出现捕获不到异常的无效测试
内容的提问来源于stack exchange,提问作者Y. Baranov
相关产品推荐
相关产品推荐

