JUnit测试中如何断言抛出特定异常
技术背景
在Java开发中,单元测试是确保代码质量和稳定性的重要手段。JUnit是Java开发中广泛使用的单元测试框架,在编写单元测试时,经常需要验证代码是否会抛出预期的异常。不同版本的JUnit以及搭配不同的断言库,有多种方式可以实现异常断言。
实现步骤
JUnit 5 和 JUnit 4.13+
可以使用 Assertions.assertThrows()
(JUnit 5)和 Assert.assertThrows()
(JUnit 4.13+):
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
规则:
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 方式
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 断言库
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 异常断言
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
规则
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 异常断言
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
产生名称冲突,需要注意导入正确的包。