junit 5 的 assertthrows() 可捕获并验证自定义异常类型、消息、业务字段、嵌套原因及构造逻辑,结合 mockito 还可测试异常触发后的系统响应。

直接用 JUnit 5 的 assertThrows() 就能确认自定义异常是否被正确抛出,关键不是只看“有没有抛”,而是拿到异常实例后逐项验证它是否符合设计预期。
用 assertThrows 捕获并确认类型
这是最基础也最关键的一步。它不依赖 try-catch,语法简洁且失败提示明确:
- 必须传 Lambda 表达式,比如
() -> service.process(input),不能直接写service.process(input) - 返回值是捕获到的异常对象,要赋给变量,例如
MyException e = assertThrows(MyException.class, ...) - 如果方法没抛这个异常,或抛的是别的类型(比如 RuntimeException),测试直接失败
校验异常消息和业务字段
只断言类型远远不够,业务异常通常带 message、errorCode、httpStatus 等字段,这些都要显式验证:
- 用
assertEquals("ERR_001", e.getErrorCode())断言错误码 - 用
assertEquals("用户名不能为空", e.getMessage())断言完整消息,避免用contains()导致误判 - 如果消息含动态内容(如 ID、金额),可配合正则:
assertTrue(e.getMessage().matches("余额不足: \d+\.\d+")) - 有 getter 方法就调用它,不要链式空指针调用,比如先
assertNotNull(e.getDetails())再断言细节
覆盖 cause 和构造逻辑
当自定义异常支持封装底层异常(比如包装 SQLException),或在构造时做了参数校验/初始化,测试也要跟上:
- 验证嵌套原因:
assertThat(e.getCause()).isInstanceOf(SQLException.class) - 检查构造器是否完整传递了原始参数,例如
assertEquals("U-123", e.getUserId()) - 若异常由 Builder 或工厂方法创建,测试需覆盖该路径,确保字段没漏赋值
模拟异常触发业务响应
真实场景中,异常往往引发降级、回滚或封装成统一响应。测试要验证整个链条是否走通:
- 用 Mockito 的
doThrow(new MyException("...")).when(mock).method()模拟服务故障 - 调用被测方法后,检查返回结果的 code 字段是否为 500、message 是否含友好提示
- 避免只测“抛没抛”,要测“抛了之后系统怎么反应”——比如事务是否回滚、日志是否记录、监控指标是否上报
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











