你能简要说明一下JUnit 5中的@TestInstance
注解以及它如何有用吗?
我认为我们可能可以通过将我们的字段设为静态的static
来实现相同的效果。
你能简要说明一下JUnit 5中的@TestInstance
注解以及它如何有用吗?
我认为我们可能可以通过将我们的字段设为静态的static
来实现相同的效果。
我认为文档提供了一个有用的概述:
如果您希望JUnit Jupiter在同一个测试实例上执行所有测试方法,只需使用@TestInstance(Lifecycle.PER_CLASS)为测试类加注释。使用此模式时,每个测试类将创建一个新的测试实例。因此,如果您的测试方法依赖于存储在实例变量中的状态,则可能需要在@BeforeEach或@AfterEach方法中重置该状态。
与默认的“per-method”模式相比,“per-class”模式具有一些额外的好处。特别是,在“per-class”模式下,可以在非静态方法以及接口默认方法上声明@BeforeAll和@AfterAll。因此,“per-class”模式还使得在@Nested测试类中使用@BeforeAll和@AfterAll方法成为可能。
但你可能已经读过了这些内容,并且正确地认为将字段声明为静态字段将产生与声明其为实例变量并使用 @TestInstance(Lifecycle.PER_CLASS)
相同的效果。
因此,也许回答“在JUnit 5中如何有用”的问题的答案是使用 @TestInstance
...
@TestInstance
则不太可能是意外或粗心的复制粘贴结果。为了减少在运行单元测试时创建的对象数量,引入了这个注解。
将@TestInstance(TestInstance.Lifecycle.PER_CLASS)
添加到你的测试类中,可以避免为每个测试创建一个新的类实例。
当你在同一个测试类中有很多测试,并且实例化这个类是昂贵的时候,这个特性尤其有用。
应该谨慎使用这个注解。所有的单元测试应该是隔离的,彼此独立的。如果其中一个测试改变了测试类的状态,那么就不应该使用这个特性。
将字段设为静态以达到相同效果并不是一个好主意。虽然它确实减少了创建的对象数量,但当执行测试类中的所有测试时,无法清理这些对象。这可能会在你有一个庞大的测试套件时引发问题。
@TestInstance
用于配置已注释的测试类或测试接口的测试实例生命周期:
PER_CLASS
:每个测试类将创建一个新的测试实例。PER_METHOD
:每个测试方法、测试工厂方法或测试模板方法将创建一个新的测试实例。此模式类似于JUnit版本1到4中的行为。如果未在测试类或由测试类实现的测试接口上显式声明@TestInstance
,则生命周期模式会隐式默认为PER_METHOD
。
PER_CLASS
将启用以下功能:
@BeforeAll
和@AfterAll
方法之间共享测试实例状态。@Nested
测试类中声明@BeforeAll
和@AfterAll
方法。@BeforeAll
和@AfterAll
。@BeforeAll
和@AfterAll
方法。由于没有人提供适当的编码示例,因此我想提供以下简单的代码示例来理解该概念。
每种方法示例-Junit5中的默认选项请注意,两种方法都是静态的,否则会引发异常,因为每种方法都实例化类。
@TestInstance(Lifecycle.PER_METHOD)
public class MathUtilTestPerMethod {
MathUtil util;
@BeforeAll
static void beforeAllInit() {
System.out.println("running before all");
}
@AfterAll
static void afterAllCleanUp() {
System.out.println("running after all");
}
@BeforeEach
void init() {
util = new MathUtil();
System.out.println("running before each...");
}
@AfterEach
void cleanUp() {
System.out.println("running after each...");
}
@Test
void testSum() {
assertEquals(2, util.addtwoNumbers(1, 1));
}
}
每个类别的样本 请注意,这两个方法中都已经移除了 static 关键字,并且 MathUtil 对象在全局范围内创建,而不是在某个方法中创建,因为类只会实例化一次。
@TestInstance(Lifecycle.PER_CLASS)
public class MathUtilTestPerClass {
MathUtil util = new MathUtil();
@BeforeAll
void beforeAllInit() {
System.out.println("running before all");
}
@AfterAll
void afterAllCleanUp() {
System.out.println("running after all");
}
@BeforeEach
void init() {
System.out.println("running before each...");
}
@AfterEach
void cleanUp() {
System.out.println("running after each...");
}
@Test
void testSum() {
assertEquals(2, util.addtwoNumbers(1, 1));
}
}
当在Kotlin中编写测试时(因为它没有静态方法),这也非常有用。
因此,不要使用带有@JvmStatic
函数的伴生对象来进行@BeforeAll
或@AfterAll
,而是将生命周期设置为PER_CLASS
并使用@BeforeAll
或@AfterAll
注释常规方法:
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyTest {
@BeforeAll
fun setup() {
println("I am invoked only once")
}
}
@MethodSource
的工厂函数(而不是将工厂函数包装在companion object {}
中,并标记工厂函数为@JvmStatic
)。@ParameterizedTest
@MethodSource("generateStrings")
fun `Test strings`(argument: String) {
check(argument.isNotEmpty())
}
private fun generateStrings() = listOf(
"java",
"kotlin"
)
使用这种方法时,请注意在必要时在@BeforeEach
或@AfterEach
函数中重置实例变量。
感谢本文的帮助。
@TestInstance(PER_CLASS)
应该是 Jupiter 中的默认设置。它可以产生更好、更具弹性的测试代码,并且与其他选择相比只有优势而无劣势。 - jannis