Java单元测试验证异常有三种方式:1. JUnit 4的@Test(expected=...)仅校验类型;2. try-catch+fail()可校验消息和多断言;3. JUnit 5的assertThrows最推荐,支持链式断言和lambda。

在 Java 单元测试中验证方法是否抛出期望异常,核心是捕获并断言异常类型和(可选)消息,避免测试因未处理异常而直接失败。
使用 JUnit 4 的 @Test(expected = XxxException.class)
最简单的方式,适用于只需验证异常类型、不关心异常消息或堆栈的场景:
- 直接在
@Test注解中指定预期异常类 - 测试方法执行时若未抛出该异常,测试失败;若抛出其他异常或正常结束,也失败
- 缺点:无法校验异常消息、无法在异常抛出后继续写其他断言
@Test(expected = IllegalArgumentException.class)
public void testDivideByZero() {
calculator.divide(10, 0); // 应抛出 IllegalArgumentException
}
使用 JUnit 4/5 的 try-catch + Assert
更灵活、推荐的做法,尤其适合需要检查异常消息、字段或进行多断言的场景:
- 手动用 try-catch 包裹被测代码
- catch 中用
assertEquals或assertTrue验证异常类型和消息 - 务必在 try 块末尾加
fail(),确保没抛异常时测试失败
@Test
public void testParseInvalidDate() {
try {
DateParser.parse("invalid-date");
fail("Expected IllegalArgumentException to be thrown");
} catch (IllegalArgumentException e) {
assertEquals("Invalid date format", e.getMessage());
}
}
使用 JUnit 5 的 assertThrows(推荐)
JUnit 5 提供了类型安全、简洁且功能完整的断言方式:
立即学习“Java免费学习笔记(深入)”;
-
assertThrows()返回捕获的异常对象,可链式校验其类型、消息、cause 等 - 无需手动 fail,逻辑更清晰,支持 lambda 表达式
- 支持嵌套异常断言(如检查 cause 是否为特定类型)
@Test
public void testCreateUserWithNullName() {
Throwable exception = assertThrows(NullPointerException.class, () -> {
userService.createUser(null, "email@example.com");
});
assertTrue(exception.getMessage().contains("name cannot be null"));
}
避免常见陷阱
实际编写时容易忽略的关键点:
- 不要只依赖 IDE 自动生成的 try-catch 模板——漏掉
fail()会导致异常未抛出时测试仍通过 - 避免用
instanceof替代精确类型断言,否则可能误判父类异常(如捕获RuntimeException却放过了子类) - 对自定义异常,优先断言具体子类而非通用异常类型
- 若方法本应抛出受检异常,确保测试方法声明 throws 或在 lambda 中正确处理


















