JUnit测试中如何断言抛出特定异常

JUnit测试中如何断言抛出特定异常

技术背景

在Java开发中,单元测试是确保代码质量和稳定性的重要手段。JUnit是Java开发中广泛使用的单元测试框架,在编写单元测试时,经常需要验证代码是否会抛出预期的异常。不同版本的JUnit以及搭配不同的断言库,有多种方式可以实现异常断言。

实现步骤

JUnit 5 和 JUnit 4.13+

可以使用 Assertions.assertThrows()(JUnit 5)和 Assert.assertThrows()(JUnit 4.13+):

1
2
3
4
5
6
7
8
9
10
11
12
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;

public class ExceptionTestingExample {
@Test
void exceptionTesting() {
ArithmeticException exception = assertThrows(ArithmeticException.class, () ->
calculator.divide(1, 0));

assertEquals("/ by zero", exception.getMessage());
}
}

JUnit 4.7+

可以使用 ExpectedException 规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class FooTest {
@Rule
public final ExpectedException exception = ExpectedException.none();

@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();

exception.expect(IndexOutOfBoundsException.class);
foo.doStuff();
}
}

经典的 try/catch 方式

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.junit.Test;
import static org.junit.Assert.fail;

public class ExceptionTestingExample {
@Test
public void testFooThrowsIndexOutOfBoundsException() {
try {
foo.doStuff();
fail("My method didn't throw when I expected it to");
} catch (IndexOutOfBoundsException expectedException) {
}
}
}

使用 AssertJ 断言库

1
2
3
4
5
6
7
8
9
10
11
12
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;

public class ExceptionTestingExample {
@Test
public void testFooThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();

assertThatThrownBy(() -> foo.doStuff())
.isInstanceOf(IndexOutOfBoundsException.class);
}
}

核心代码

以下是不同方式的核心代码片段:

JUnit 5 异常断言

1
2
3
4
5
6
7
8
9
10
11
12
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;

public class JUnit5ExceptionTest {
@Test
void testException() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
}

JUnit 4 ExpectedException 规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class JUnit4ExpectedExceptionTest {
@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void testReadFile() throws FileNotFoundException {
thrown.expect(FileNotFoundException.class);
thrown.expectMessage(startsWith("The file test.txt"));
myClass.readFile("test.txt");
}
}

AssertJ 异常断言

1
2
3
4
5
6
7
8
9
10
11
12
13
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;

public class AssertJExceptionTest {
@Test
public void testException() {
assertThatThrownBy(() -> {
throw new Exception("boom!");
})
.isInstanceOf(Exception.class)
.hasMessageContaining("boom");
}
}

最佳实践

  • JUnit 版本选择:如果使用 JUnit 5 或 JUnit 4.13+,优先使用 assertThrows 方法,它简洁且功能强大。
  • 异常信息验证:除了验证异常类型,还可以验证异常的消息、原因等信息,确保异常的准确性。
  • 代码可读性:选择合适的断言方式,使测试代码具有良好的可读性和可维护性。

常见问题

1. 使用 @Test(expected = ...) 的问题

这种方式只能断言方法抛出了指定异常,但不能精确到特定代码行。如果测试代码中多个地方可能抛出相同异常,可能会导致误判。

2. ExpectedException 规则的使用问题

  • ExpectedException 实例必须是 public 的。
  • 不能在 @Before 方法中实例化 ExpectedException

3. 名称冲突问题

使用 AssertJ 时,assertThat 可能会与 JUnit 的 assertThat 产生名称冲突,需要注意导入正确的包。


JUnit测试中如何断言抛出特定异常
https://119291.xyz/posts/2025-04-24.junit-exception-assertion-guide/
作者
ww
发布于
2025年4月24日
许可协议