Parasoft:谈谈迁移到Junit 5以获取最新技术的好处
自JUnit 5发行以来已经有几年了。如果您尚未开始将其用于开发测试,则应该这样做。 JUnit 5具有许多新功能和改进功能,可以节省您的时间和麻烦。让我们看一下如何开始使用JUnit 5以获得最新技术的好处。
从JUnit 4迁移到JUnit 5的原因
如果您使用JUnit 4已有一段时间,那么迁移测试似乎是一项艰巨的任务。好消息是您可能不需要转换任何测试——JUnit 5可以使用Vintage库运行JUnit 4测试,因此您可以开始使用JUnit 5编写新的测试。
这是开始使用JUnit 5的四个基本理由:
- JUnit 5利用了Java 8或更高版本的功能(例如lambda函数),使测试更强大且更易于维护。
- JUnit 5添加了一些非常有用的新功能,用于描述、组织和执行测试。例如,测试可以获得更好的显示名称,并且可以按层次进行组织。
- JUnit 5被组织为多个库,因此仅将您需要的功能导入到项目中。使用Maven和Gradle之类的构建系统,包含正确的库很容易。
- JUnit 5一次只能使用一个以上的扩展,而JUnit 4则不能(一次只能使用一个运行程序)。这意味着您可以轻松地将Spring扩展与其他扩展(例如您自己的自定义扩展)结合在一起。
如何迁移到JUnit 5
即使您已有现有的JUnit 4测试,从JUnit 4切换到JUnit 5也非常简单。除非需要新功能,否则大多数组织不需要将旧的JUnit转换为JUnit 5。
- 将您的库和构建系统从JUnit 4更新到JUnit5。请确保在测试运行时路径中包括JUnit-vintage-engine工件,以允许执行现有测试。
- 开始使用新的JUnit 5构造构建新的测试。
- (可选)将JUnit 3和JUnit 4测试转换为JUnit 5。
JUnit 5和JUnit 4之间的重要区别
JUnit 5测试看起来与JUnit 4几乎相同,但是您应该注意一些差异。
输入
JUnit 5将新的org.JUnit.jupiter包用于其注释和类。例如,org.JUnit.Test成为org.JUnit.jupiter.api.Test。
注解
@Test注释不再具有参数;这些都已移至某个功能。例如,要指示期望测试在JUnit 4中引发异常:
在JUnit 5中,它已更改为:
同样,超时也已更改。在JUnit 4中,它们如下所示:
在JUnit 5中,超时如下所示:
以下是其他已更改的注释:
- @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匹配器以匹配条件。满足条件时,可以使用Lambda(类型为Executable)来执行代码。
这是JUnit 4中的示例:
在JUnit 5中,它变为:
扩展JUnit
在JUnit 4中,自定义框架通常意味着使用@RunWith批注指定自定义运行器。使用多个运行程序是有问题的,通常需要链接或使用@Rule。使用扩展在JUnit 5中对此进行了简化和改进。
例如,在JUnit 4中使用Spring框架构建测试如下所示:
使用JUnit 5,您可以改为包含Spring Extension:
@ExtendWith注释是可重复的,这意味着可以轻松组合多个扩展名。
您还可以通过创建一个实现org.JUnit.jupiter.api.extension中的一个或多个接口的类,然后使用@ExtendWith将其添加到我们的测试中,来轻松定义我们自己的自定义扩展。
将测试转换为JUnit 5
要将现有的JUnit 3或JUnit 4测试转换为JUnit 5,以下步骤应适用于大多数测试:
- 更新导入以删除JUnit 4并添加JUnit5。例如,更新@Test批注的程序包名称,以及断言的程序包和类名(从断言到断言)。如果有编译错误,请不要担心,完成以下步骤即可解决这些错误。
- 用新的全局替换旧的注释和类名。例如,将所有@Before替换为@BeforeEach,并将所有Asserts替换为Assertions。
- 更新断言。提供消息的任何断言都需要将message参数移到末尾。当所有三个参数都是字符串时,请特别注意!另外,更新超时和预期的异常(请参阅上面的示例)。
- 如果您要使用假设,请进行更新。
- 用适当的@ExtendWith注释替换@ RunWith,@ Rule或@ClassRule的所有实例。您可能需要在线获取有关示例扩展的更新文档。
请注意,迁移参数化测试将需要更多的重构,尤其是如果您一直在使用JUnit 4参数化(JUnit 5参数化测试的格式更接近JUnitParams)。
新功能
到目前为止,我仅讨论了现有功能及其更改方式。但是JUnit 5提供了许多新功能,使我们的测试更具描述性和可维护性。
显示名称
使用JUnit 5,可以将@DisplayName批注添加到类和方法。生成报告时使用该名称,这使描述测试的目的以及跟踪故障更加容易,例如:
您还可以使用显示名称生成器来处理您的测试类和/或方法以生成您喜欢的任何格式的测试名称。有关详细信息和示例,请参见JUnit文档。
断言
JUnit 5引入了一些新的断言,例如:
- assertIterableEquals()使用equals()对两个可迭代对象进行深入验证
- assertLinesMatch()验证两个字符串列表是否匹配;它在“expected”参数中接受正则表达式。
- assertAll()将多个断言组合在一起。额外的好处是,即使单个断言失败,也将执行所有断言。
- assertThrows()和assertDoesNotThrow()已替换@Test批注中的预期属性
嵌套测试
JUnit 4中的测试套件很有用,但是JUnit 5中的嵌套测试更易于设置和维护,它们可以更好地描述测试组之间的关系,例如:
在上面的示例中,您可以看到我对与MyClass相关的所有测试使用了单个类。我可以验证该类在外部测试类中是否可实例化,并且我对所有实例化和初始化MyClass的测试使用嵌套的内部类。@BeforeEach方法仅适用于嵌套类中的测试。
测试和类的@DisplayNames批注指示测试的目的和组织。这有助于了解测试报告,因为您可以看到执行测试的条件(使用初始化验证MyClass)以及测试正在验证的内容(myMethod返回true)。这是用于JUnit 5的良好测试设计模式。
参数化测试
JUnit 4中使用内置库(例如JUnit4Parameterized)或第三方库(例如JUnitParams)进行测试参数化。在JUnit 5中,参数化测试是完全内置的,并采用了JUnit4Parameterized和JUnitParams的一些最佳功能,例如:
格式类似于JUnitParams,其中参数直接传递到测试方法。请注意,要测试的值可以来自几个不同的来源。在这里,我只有一个参数,因此使用@ValueSource很容易。@EmptySource和@NullSource表示我想分别向运行的值列表添加一个空字符串和一个null(如果同时使用它们,则可以如上所述将它们组合在一起)。还有多个其他值源,例如@EnumSource和@ArgumentsSource(自定义值提供程序)。如果需要多个参数,则也可以使用@MethodSource或@CsvSource。有关更多详细信息和示例,请参见JUnit 5文档。
JUnit 5中添加的另一种测试类型是@RepeatedTest,其中将单个测试重复指定的次数。
有条件的测试执行
JUnit 5提供了ExecutionCondition扩展API,以有条件地启用或禁用测试或容器(测试类)。这就像在测试中使用@Disabled一样,但是它可以定义自定义条件。有多个内置条件,例如:
- @EnabledOnOs和@DisabledOnOs:仅在指定的操作系统上启用测试。
- @EnabledOnJre和@DisabledOnJre:指定应该为特定版本的Java启用或禁用测试。
- @EnabledIfSystemProperty:基于JVM系统属性的值启用测试。
- @EnabledIf:如果符合脚本条件,则使用脚本逻辑来启用测试。
测试模板
测试模板不是常规测试。它们定义了一组要执行的步骤,然后可以使用特定的调用上下文在其他位置执行这些步骤。这意味着您可以一次定义一个测试模板,然后在运行时构建一个调用上下文列表以运行该测试。在Junit 5文档中找到更多详细信息和示例。
动态测试
动态测试就像测试模板一样,要运行的测试是在运行时生成的。但是,虽然测试模板是通过一组特定的步骤定义的并且可以多次运行,但是动态测试使用相同的调用上下文,但是可以执行不同的逻辑。动态测试的一种用途是流式传输抽象对象列表,并根据其具体类型对每个对象执行一组独立的断言。有关良好的示例,请参见Junit 5文档。
结论
JUnit 5是对JUnit框架的强大而灵活的更新。它提供了各种改进和新功能来组织和描述测试用例,并有助于理解测试结果。更新JUnit 5既快速又容易——只需更新项目依赖项并开始使用新功能。