作者 | Brian McGlauflin
译者 | IT外文选刊
改进和新功能使JUnit 5引人注目。
JUnit 5是对JUnit框架的一个强大而灵活的更新,它提供了各种改进和新的功能来组织和描述测试用例,并帮助理解测试结果。升级到JUnit 5非常简单快捷:只需更新你的项目依赖关系,就可以开始使用新功能。
如果你已经使用JUnit 4有一段时间了,迁移测试可能看起来是一项艰巨的任务。好消息是,你可能不需要转换任何测试;JUnit 5可以使用Vintage库运行JUnit 4测试。
话虽如此,但以下是开始使用JUnit 5编写新测试用例的四个强有力的理由:
- JUnit 5利用了Java 8或更高版本的特性,例如lambda函数,使测试更强大,更容易维护。
- JUnit 5为描述、组织和执行测试添加了一些非常有用的新功能。例如,测试得到了更好的显示名称,并且可以分层组织。
- JUnit 5被组织成多个库,所以只将你需要的功能导入到你的项目中。通过Maven和Gradle等构建系统,包含合适的库很容易。
- JUnit 5可以同时使用多个扩展,这是JUnit 4无法做到的(一次只能使用一个runner)。这意味着你可以轻松地将Spring扩展与其他扩展(如你自己的自定义扩展)结合起来。
从JUnit 4切换到JUnit 5是非常简单的,即使你有现有的JUnit 4测试,也是如此。大多数组织不需要将旧的JUnit测试转换为JUnit 5的测试,除非需要新的功能。在这种情况下,使用这些步骤:
- 将你的库和构建系统从JUnit 4更新到JUnit 5。确保在你的测试运行时路径中包含 junit-vintage-engine 工件,以允许你现有的测试执行。
- 使用新的JUnit 5构造开始构建新的测试。
- (可选)将JUnit测试用例转换为JUnit 5的测试用例。
重要的区别
JUnit 5的测试看起来和JUnit 4的测试基本相同,但有几个不同之处你应该注意。
导入
JUnit 5 使用新的 org.junit.jupiter 包。例如,org.junit.junit.Test变成了org.junit.jupiter.api.Test。
注解
@Test 注解不再有参数,每个参数都被移到了一个函数中。例如,下面是如何在JUnit 4中表示预计一个测试会抛出异常的方法:
@Test(expected = Exception.class) public void testThrowsException() throws Exception { // ... }
在Junit5中,这个写法被改成了如下:
@Test void testThrowsException() throws Exception { Assertions.assertThrows(Exception.class, () -> { //... }); }
同样,超时也发生了变化。下面是JUnit 4中的一个例子:
@Test(timeout = 10) public void testFailWithTimeout() throws InterruptedException { Thread.sleep(100); }
在JUnit5中,它被改成了如下:
@Test void testFailWithTimeout() throws InterruptedException { Assertions.assertTimeout(Duration.ofMillis(10), () -> Thread.sleep(100)); }
以下是其他的注解的变化:
@Before变成了@BeforeEach。
@After变成了@AfterEach。
@BeforeClass变成了@BeforeAll。
@AfterClass变成了@AfterAll。
@Ignore变成了@Disabled。
@Category变成了@Tag。
@Rule和@ClassRule没有了,用@ExtendWith和@RegisterExtension代替。
断言
JUnit 5断言现在在org.junit.jupiter.api.Assertions中。大多数常见的断言,如assertEquals()和assertNotNull(),看起来和以前一样,但有一些不同。
- 错误信息现在是最后一个参数,例如:assertEquals(“my message”,1,2)现在是assertEquals(1,2,”my message”)。
- 大多数断言现在接受一个构造错误信息的lambda,只有当断言失败时才会被调用。
- assertTimeout()和 assertTimeoutPreemptively()取代了 @Timeout 注释(在 JUnit 5 中有一个 @Timeout 注释,但它的工作方式与 JUnit 4 中不同)。
有几个新的断言,如下文所述。
请注意,如果你愿意,你可以在JUnit 5测试中继续使用JUnit 4中的断言。
假设
假设已被移至 org.junit.jupiter.api.Assumptions。
同样的假设也存在,但它们现在支持BooleanSupplier以及Hamcrest匹配器(http://hamcrest.org/)来匹配条件。Lambdas(类型为 Executable)可以用来在条件满足时执行代码。
例如,这里是JUnit 4中的一个例子:
@Test public void testNothingInParticular() throws Exception { Assume.assumeThat("foo", is("bar")); assertEquals(...); }
在Junit5中,它变成了这样:
@Test void testNothingInParticular() throws Exception { Assumptions.assumingThat("foo".equals(" bar"), () -> { assertEquals(...); }); }
扩展JUnit
在JUnit 4中,自定义框架通常意味着使用@RunWith注释来指定一个自定义的运行器。使用多个运行器是有问题的,通常需要链式或使用@Rule。在JUnit 5中,这一点已经得到了简化和改进。
例如,在JUnit 4中,使用Spring框架构建测试看起来是这样的:
@RunWith(SpringJUnit4ClassRunner.class) public class MyControllerTest { // ... }
在JUnit 5中,你可以用Spring扩展来代替:
@ExtendWith(SpringExtension.class) class MyControllerTest { // ... }
@ExtendWith 注解是可重复的,这意味着多个扩展可以很容易地组合在一起。
你也可以通过创建一个类来实现org.junit.jupiter.api.extendWith中的一个或多个接口,然后用@ExtendWith将其添加到你的测试中,从而轻松定义你自己的自定义扩展。
将测试转换到JUnit 5
要将现有的JUnit 4测试转换为JUnit 5,请使用以下步骤,这应该对大多数测试都有效。
- 更新导入,删除JUnit 4并添加JUnit 5。例如,更新@Test注解的包名,更新断言的包名和类名(从Asserts到Assertions)。如果有编译错误也不要担心,因为完成下面的步骤应该可以解决。
- 在全局中用新的注解和类名替换旧的注解和类名。例如,将所有的@Before替换为@BeforeEach,将所有的Asserts替换为Assertions。
- 更新断言;任何提供消息的断言都需要将消息参数移到最后(当三个参数都是字符串时要特别注意!)。另外,更新超时和预期异常(见上面的例子)。
- 更新假设,如果你正在使用它们。
- 用适当的 @ExtendWith 注释替换 @RunWith、@Rule 或 @ClassRule 的任何实例。你可能需要在网上找到你所使用的扩展实例的更新文档。
注意,迁移参数化测试需要更多的重构,特别是如果你一直在使用JUnit 4参数化测试(JUnit 5参数化测试的格式更接近于JUnitParams),那么迁移参数化测试需要更多的重构。
新功能
到目前为止,我只讨论了现有的功能以及它的变化。但JUnit 5提供了大量的新功能,让你的测试更具有描述性和可维护性。
显示名称
使用JUnit 5,你可以在类和方法中添加@DisplayName注释。这个名称在生成报告时使用,这使得描述测试的目的和追踪失败更容易,比如说:
@DisplayName("Test MyClass") class MyClassTest { @Test @DisplayName("Verify MyClass.myMethod returns true") void testMyMethod() throws Exception { // ... } }
你也可以使用显示名称生成器来处理你的测试类或方法,以你喜欢的任何格式生成测试名称。请参阅JUnit文档中的具体内容和示例。
断言
JUnit 5引入了一些新的断言,比如以下这些:
- assertIterableEquals()使用equals()对两个迭代项进行深度验证。
- assertLinesMatch()验证两个字符串列表是否匹配;它接受期望参数中的正则表达式。
- assertAll() 将多个断言分组在一起。附加的好处是所有的断言都会被执行,即使单个断言失败。
- assertThrows()和 assertDoesNotThrow()取代了 @Test 注释中的预期属性。
嵌套测试
JUnit 4中的测试套件是很有用的,但JUnit 5中的嵌套测试更容易设置和维护,它们能更好地描述测试组之间的关系,比如说:
@DisplayName("Verify MyClass") class MyClassTest { MyClass underTest; @Test @DisplayName("can be instantiated") public void testConstructor() throws Exception { new MyClass(); }
@Nested @DisplayName("with initialization") class WithInitialization { @BeforeEach void setup() { underTest = new MyClass(); underTest.init("foo"); } @Test @DisplayName("myMethod returns true") void testMyMethod() { assertTrue(underTest.myMethod()); } } }
在上面的例子中,你可以看到,我对所有与MyClass相关的测试都使用一个类。我可以验证该类在外部测试类中是可以实例化的,而我对所有MyClass被实例化和初始化的测试都使用嵌套的内部类。@BeforeEach方法只适用于嵌套类中的测试。
测试和类的@DisplayNames注解表示了测试的目的和组织方式。这有助于理解测试报告,因为你可以看到测试是在什么条件下执行的(初始化后验证MyClass),以及测试要验证什么(myMethod返回true)。这是JUnit 5的一个很好的测试设计模式。
测试的参数化
测试参数化在JUnit 4中就已经存在,有内置的库如JUnit4Parameterized或第三方库如JUnitParams等。在JUnit 5中,参数化测试完全内置,并采用了JUnit4Parameterized和JUnitParams等一些最好的特性。例子:
@ParameterizedTest @ValueSource(strings = {"foo", "bar"}) @NullAndEmptySource void myParameterizedTest(String arg) { underTest.performAction(arg); }
其格式看起来像JUnitParams,其中参数直接传递给测试方法。注意,要测试的值可以来自多个不同的来源。这里,我只用了一个参数,所以使用@ValueSource很方便。@EmptySource和@NullSource分别表示你要在要运行的值列表中添加一个空字符串和一个空值(如果你使用这两个值,你可以把它们组合在一起,如上所示)。还有其他多个值源,比如@EnumSource和@ArgumentsSource(一种自定义值提供者)。如果你需要一个以上的参数,也可以使用@MethodSource或@CsvSource。
在JUnit 5中添加的另一个测试类型是@RepeatedTest,在这里,一个测试被重复指定次数的测试。
测试执行条件
JUnit 5 提供了 ExecutionCondition 扩展 API 来有条件地启用或禁用一个测试或容器(测试类)。这就像在测试上使用@Disabled一样,但它可以定义自定义条件。有多个内置的条件,比如说这些条件:
- @EnabledOnOs和@DisabledOnOs。仅在指定的操作系统上启用或禁用测试。
- @EnabledOnJre和@DisabledOnJre。指定特定版本的Java测试应该启用或禁用。
- @EnabledIfSystemProperty: 启用基于JVM系统属性值的测试。
- @EnabledIf: 使用脚本逻辑,在满足脚本条件的情况下,使用脚本逻辑启用测试。
测试模板
测试模板不是常规测试;它们定义了一组要执行的步骤,然后可以在其他地方使用特定的调用上下文执行。这意味着你可以定义一次测试模板,然后在运行时建立一个调用上下文列表来运行该测试。有关详细信息和示例,请参阅文档。
动态测试
动态测试就像测试模板,要运行的测试是在运行时生成的。然而,测试模板是用一组特定的步骤来定义并运行多次,而动态测试使用相同的调用上下文,但可以执行不同的逻辑。动态测试的一个用途是将一个抽象对象的列表流化,并根据它们的具体类型为每个对象执行一组单独的断言。文档中有一些很好的例子。
结论
尽管你可能不需要将旧的JUnit 4测试转换为JUnit 5,除非你想使用新的JUnit 5功能,但也有令人信服的理由让你切换到JUnit 5。例如,JUnit 5的测试功能更强大,更容易维护。此外,JUnit 5提供了许多有用的新特性,只有你使用的特性才会被导入,你可以使用多个扩展,甚至可以创建自己的自定义扩展。这些变化和新功能一起,为JUnit框架提供了一个强大而灵活的更新。
外文链接:
https://blogs.oracle.com/javamagazine/post/migrating-from-junit-4-to-junit-5-important-differences-and-benefits
标签:断言,void,测试,使用,Test,迁移,转载,JUnit From: https://www.cnblogs.com/xupeixuan/p/16931418.html