JUnit 5 是一个强大且灵活的测试框架,它支持多种功能,比如注解、参数化测试、生命周期管理、条件化测试等。以下是一些经典的 JUnit 5 使用案例,涵盖了不同的测试场景:
1. 基础的单元测试
最常见的单元测试用例是验证方法的正确性。可以使用 @Test
注解来标记测试方法。
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class CalculatorTest {
@Test
void testAdd() {
Calculator calculator = new Calculator();
assertEquals(5, calculator.add(2, 3));
}
@Test
void testSubtract() {
Calculator calculator = new Calculator();
assertEquals(1, calculator.subtract(3, 2));
}
}
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class CalculatorTest {
@Test
void testAdd() {
Calculator calculator = new Calculator();
assertEquals(5, calculator.add(2, 3));
}
@Test
void testSubtract() {
Calculator calculator = new Calculator();
assertEquals(1, calculator.subtract(3, 2));
}
}
2. 前置和后置处理(@BeforeAll 和 @BeforeEach)
@BeforeAll
和 @BeforeEach
是生命周期管理注解,用于设置和清理测试前后的一些工作。
@BeforeAll
:在所有测试方法之前执行一次(静态方法)。@BeforeEach
:在每个测试方法之前执行。
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class LifecycleTest {
@BeforeAll
static void setupBeforeAll() {
System.out.println("Executed before all tests.");
}
@BeforeEach
void setupBeforeEach() {
System.out.println("Executed before each test.");
}
@Test
void test1() {
System.out.println("Running test1");
}
@Test
void test2() {
System.out.println("Running test2");
}
}
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class LifecycleTest {
@BeforeAll
static void setupBeforeAll() {
System.out.println("Executed before all tests.");
}
@BeforeEach
void setupBeforeEach() {
System.out.println("Executed before each test.");
}
@Test
void test1() {
System.out.println("Running test1");
}
@Test
void test2() {
System.out.println("Running test2");
}
}
3. 条件化测试(@EnabledIf 和 @DisabledIf)
JUnit 5 支持条件化执行测试。可以基于一些条件(例如操作系统、JVM版本、系统环境变量等)启用或禁用测试。
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
class ConditionalTest {
@Test
@EnabledOnOs(OS.WINDOWS)
void testOnlyOnWindows() {
System.out.println("This test runs only on Windows OS.");
}
@Test
@EnabledOnOs(OS.LINUX)
void testOnlyOnLinux() {
System.out.println("This test runs only on Linux OS.");
}
}
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
class ConditionalTest {
@Test
@EnabledOnOs(OS.WINDOWS)
void testOnlyOnWindows() {
System.out.println("This test runs only on Windows OS.");
}
@Test
@EnabledOnOs(OS.LINUX)
void testOnlyOnLinux() {
System.out.println("This test runs only on Linux OS.");
}
}
4. 参数化测试(@ParameterizedTest)
参数化测试使得同一个测试方法可以多次运行,每次使用不同的参数值。
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.ParameterizedTest;
import org.junit.jupiter.api.ValueSource;
class ParameterizedTestExample {
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testIsPositive(int number) {
assertTrue(number > 0);
}
}
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.ParameterizedTest;
import org.junit.jupiter.api.ValueSource;
class ParameterizedTestExample {
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testIsPositive(int number) {
assertTrue(number > 0);
}
}
使用 @ValueSource
来提供多个值并自动执行多次测试。可以用 @CsvSource
或 @MethodSource
来提供更复杂的数据源。
5. 异常测试(@Test + assertThrows)
用 assertThrows
来验证方法是否抛出预期的异常。
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class ExceptionTest {
@Test
void testException() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("Invalid argument");
});
assertEquals("Invalid argument", exception.getMessage());
}
}
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class ExceptionTest {
@Test
void testException() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("Invalid argument");
});
assertEquals("Invalid argument", exception.getMessage());
}
}
6. 组合多个条件的测试(@Test + @Tag)
@Tag
可以用来标记测试,并按标签选择运行特定的测试。
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
class TagTest {
@Test
@Tag("fast")
void testFast() {
System.out.println("This is a fast test.");
}
@Test
@Tag("slow")
void testSlow() {
System.out.println("This is a slow test.");
}
}
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
class TagTest {
@Test
@Tag("fast")
void testFast() {
System.out.println("This is a fast test.");
}
@Test
@Tag("slow")
void testSlow() {
System.out.println("This is a slow test.");
}
}
使用 @Tag
标签,可以在运行时通过命令行参数指定运行的测试类型。例如,@Tag("fast")
只运行快速测试。
7. 测试类级别的前置和后置处理(@BeforeAll 和 @AfterAll)
@AfterAll
注解用于在所有测试方法执行完后进行清理工作。
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
class AfterAllTest {
@BeforeAll
static void setup() {
System.out.println("Executed before all tests.");
}
@AfterAll
static void cleanup() {
System.out.println("Executed after all tests.");
}
@Test
void test1() {
System.out.println("Running test1");
}
@Test
void test2() {
System.out.println("Running test2");
}
}
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
class AfterAllTest {
@BeforeAll
static void setup() {
System.out.println("Executed before all tests.");
}
@AfterAll
static void cleanup() {
System.out.println("Executed after all tests.");
}
@Test
void test1() {
System.out.println("Running test1");
}
@Test
void test2() {
System.out.println("Running test2");
}
}
8. 使用 assertAll 进行组合断言
assertAll
用于同时检查多个断言,而不是只关注第一个失败的断言。
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class AssertAllTest {
@Test
void testMultipleAssertions() {
assertAll("Test all assertions",
() -> assertEquals(4, 2 + 2),
() -> assertTrue(5 > 3),
() -> assertNotNull("Hello")
);
}
}
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class AssertAllTest {
@Test
void testMultipleAssertions() {
assertAll("Test all assertions",
() -> assertEquals(4, 2 + 2),
() -> assertTrue(5 > 3),
() -> assertNotNull("Hello")
);
}
}
9. 测试实例生命周期(@TestInstance)
JUnit 5 允许改变测试类的生命周期,默认是 PER_METHOD
(每个测试方法创建新的实例)。如果你希望测试类的生命周期是 PER_CLASS
,可以使用 @TestInstance(Lifecycle.PER_CLASS)
。
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestInstanceLifecycle {
private int count = 0;
@Test
void testCount1() {
count++;
System.out.println("Count: " + count);
}
@Test
void testCount2() {
count++;
System.out.println("Count: " + count);
}
}
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestInstanceLifecycle {
private int count = 0;
@Test
void testCount1() {
count++;
System.out.println("Count: " + count);
}
@Test
void testCount2() {
count++;
System.out.println("Count: " + count);
}
}
10. 自定义扩展(@ExtendWith)
JUnit 5 允许你创建自定义扩展来在测试生命周期的特定点插入行为。以下是一个使用 @ExtendWith
注解的简单示例:
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.Test;
@ExtendWith(MyExtension.class)
class MyTest {
@Test
void test() {
System.out.println("Test is running with custom extension.");
}
}
class MyExtension implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
System.out.println("Handling exception in test: " + throwable.getMessage());
}
}
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.Test;
@ExtendWith(MyExtension.class)
class MyTest {
@Test
void test() {
System.out.println("Test is running with custom extension.");
}
}
class MyExtension implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
System.out.println("Handling exception in test: " + throwable.getMessage());
}
}
总结
这些是 JUnit 5 中常见的一些使用案例,涵盖了测试生命周期管理、参数化测试、条件化测试、异常处理、标签化测试、组合断言等不同方面。根据不同的应用场景,可以灵活使用这些功能来提升测试的质量和可维护性。