JUnit 5 的 assertThrows 是验证方法抛出预期异常的最推荐方式,它能确认异常类型并获取实例以断言消息、错误码等字段,比 @Test(expected = ) 更安全灵活。

验证方法是否抛出预期异常,核心是让测试明确“抛出该异常 = 测试通过”,而不是任由异常未被捕获导致测试直接失败。目前最主流、推荐的方式是使用 JUnit 5 的 assertThrows,它类型安全、可链式断言、语义清晰。
用 assertThrows 断言异常类型和消息
这是当前最佳实践,适用于绝大多数场景:
- 静态导入
import static org.junit.jupiter.api.Assertions.*; - 调用
assertThrows(异常类.class, () -> { 被测代码 }),方法返回捕获的异常实例 - 直接对返回的异常对象做进一步断言,比如检查
getMessage()、自定义字段或错误码 - 示例:
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> userService.updateUser(null));<br>assertEquals("用户名不能为空", e.getMessage());
处理自定义异常时的要点
自定义异常(如 InsufficientBalanceException)同样适用 assertThrows,且优势更明显:
- 传入自定义异常的 class 对象,类型检查严格,不会误匹配父类
- 返回的对象可调用其专属 getter 方法,例如
e.getErrorCode()或e.getTimestamp() - 支持正则校验消息:
assertTrue(e.getMessage().matches("余额不足: \d+\.\d+")); - 避免在未确认异常非 null 的情况下链式调用,但
assertThrows已保证返回值不为 null
不推荐的旧方式及原因
某些写法虽能运行,但存在明显缺陷,应逐步淘汰:
立即学习“Java免费学习笔记(深入)”;
-
@Test(expected = XxxException.class)(JUnit 4):只能校验类型,无法获取异常对象,也无法控制抛出位置;若 setup 阶段意外抛同类型异常,测试可能误通过 - 手动 try-catch + fail():逻辑冗长,易漏写
fail(),且无法利用 JUnit 的断言报告机制,IDE 支持弱 -
@Rule ExpectedException(JUnit 4):已废弃,JUnit 5 中不可用,且配置繁琐
确保测试真正触发异常路径
断言本身再准确,也依赖被测代码实际执行到抛异常的分支:
- 检查输入参数或状态是否真实满足触发条件(如传负数、空字符串、模拟数据库连接失败等)
- 若涉及外部依赖(如 HTTP 调用、数据库),需用 Mockito 等工具打桩,控制返回结果以进入异常分支
- 避免测试中混入无关逻辑,保证
assertThrows包裹的代码块只包含待测行为


















