章
目
录
自JUnit 4以来,用于设置和拆卸操作的@BeforeClass和@AfterClass方法必须声明为静态方法。在JUnit 5中,此限制已经解除,我们也可以在非静态方法上使用等效的@BeforeAll和@AfterAll注解,从而提供更多的灵活性和使用实例特定的设置和清理逻辑的能力。
在本JUnit 5教程中,我们将学习编写非静态@BeforeAll和@AfterAll方法,通过使用@TestInstance(TestInstance.Lifecycle.PER_CLASS)注解我们的测试,从而在它们中访问测试实例特定的变量。
1.默认情况下,@BeforeAll和@AfterAll方法是“静态的”
在JUnit 5中,@BeforeAll和@AfterAll方法必须声明为静态方法,这意味着它们与测试类本身相关联,而不是与类的实例相关联。
有时,这可能会成为一种限制,例如,当并行运行测试时。在并行执行环境中,静态方法可能会引入并发问题,但非静态方法确保每个测试实例是隔离的。
public class TestClass {
@BeforeAll
static void setupBeforeAll() {
//所有测试之前的类特定设置逻辑
}
@AfterAll
static void cleanupAfterAll() {
// 所有测试后的类特定清理逻辑
}
@Test
void test1() {
// 测试1逻辑
}
@Test
void test2() {
// 测试2逻辑
}
}
2. 将@BeforeAll和@AfterAll方法作为非静态方法
要在JUnit 5中编写和使用非静态@BeforeAll和@AfterAll方法,我们必须使用@TestInstance(TestInstance.Lifecycle.PER_CLASS)注解测试类。这表明我们希望使用非静态测试实例生命周期。这确保JUnit为每个测试类创建一个新的测试实例。
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestClass {
//...
}
在上面的示例中,@BeforeAll和@AfterAll方法是非静态的。这使它们能够访问实例特定的数据或执行实例特定的设置和清理操作。
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestClass {
@BeforeAll
void setupBeforeAll() {
// 所有测试之前,特定实例的设置逻辑。
}
@AfterAll
void cleanupAfterAll() {
// 所有测试之后,特定实例的清理逻辑。
}
@Test
void test1() {
// Test 1 logic.
}
@Test
void test2() {
// Test 2 logic.
}
}
3. 示例
假设我们有一个购物车模块,我们想在并发环境中测试其功能。我们编写了以下测试类,该类利用购物车作为列表,并测试添加/删除操作。
我们多次并发地运行测试10次。
public class TestNonStaticBeforeAllAndAfterAll {
private static List<String> cart = new ArrayList<>();
@BeforeAll
static void setupBeforeAll() {
System.out.println("Setting up the shopping cart for all tests");
cart.add("Item 1");
cart.add("Item 2");
}
@AfterAll
static void cleanupAfterAll() {
System.out.println("Cleaning up the shopping cart after all tests");
cart.clear();
}
@RepeatedTest(10)
@Execution(ExecutionMode.CONCURRENT)
void testAddItemToCart() {
cart.add("Item 3");
// 添加商品并断言其存在于购物车的测试逻辑。
}
@RepeatedTest(10)
@Execution(ExecutionMode.CONCURRENT)
void testRemoveItemFromCart() {
cart.remove("Item 1");
cart.remove("Item 2");
cart.remove("Item 3");
// 删除商品并断言其不存在于购物车的测试逻辑。
}
}
此外,我们通过设置以下属性文件/src/test/resources/junit-platform.properties将顶级类的默认执行模式配置为并行。
junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = concurrent
junit.jupiter.execution.parallel.mode.classes.default = concurrent
现在,当我们多次运行测试类时,由于在不同的测试类实例中并发地访问购物车,几次测试将会失败。
java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 10
at java.base/java.util.ArrayList.fastRemove(ArrayList.java:724)
at java.base/java.util.ArrayList.remove(ArrayList.java:711)
at com.howtodoinjava.junit5.examples.TestNonStaticBeforeAllAndAfterAll
.testRemoveItemFromCart(TestNonStaticBeforeAllAndAfterAll.java:39)
为了解决这个问题,我们将在本文的第一部分进行推荐更改,即测试生命周期为PER_CLASS。这是更新的测试类。
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class TestNonStaticBeforeAllAndAfterAll {
private List<String> cart = new ArrayList<>();
@BeforeAll
void setupBeforeAll() {
System.out.println("Setting up the shopping cart for all tests");
cart.add("Item 1");
cart.add("Item 2");
}
@AfterAll
void cleanupAfterAll() {
System.out.println("Cleaning up the shopping cart after all tests");
cart.clear();
}
@RepeatedTest(10)
@Execution(ExecutionMode.CONCURRENT)
void testAddItemToCart() {
cart.add("Item 3");
// 添加商品并断言其存在于购物车的测试逻辑。
}
@RepeatedTest(10)
@Execution(ExecutionMode.CONCURRENT)
void testRemoveItemFromCart() {
cart.remove("Item 1");
cart.remove("Item 2");
cart.remove("Item 3");
//删除商品并断言其不存在于购物车的测试逻辑。
}
}
4.结论
在这篇JUnit 5教程中,我们学会了使用非静态的@BeforeAll和@AfterAll方法来帮助在这些设置和收尾方法中访问测试实例特定的变量。为了快速参考,我们需要用@TestInstance(TestInstance.Lifecycle.PER_CLASS)注解我们的测试。