2月初, JUnit 5(又名JUnit Lambda)团队发布了一个alpha版本。 由于JUnit 4是我工具箱中使用最频繁的项目之一,因此我认为值得一看下一个主要版本。
我试用了最新版本,并记下了我在这里发现值得注意的更改。
安装JUnit 5
不用说,一个名为JUnit Lambda的项目需要Java 1.8或更高版本。 如果给出了,那么包括库就很简单了。 可从Sonatype的快照存储库( https://oss.sonatype.org/content/repositories/snapshots/org/junit/ )获取当前Alpha发布通道的最新版本。
可以使用Maven和Gradle消耗工件。 如果您希望手动维护依赖性,那么还有一个可用的zip发行版 ,其中包含编译和运行JUnit 5的所有内容。
在开发时,仅依赖org.junit:junit5-api
模块就足够了。
请注意,在指定快照存储库时,应将其配置为从不缓存工件,以便始终使用最新版本。
从JUnit 4减轻负担
据我所知,新版本是对库的完全重写,对旧版本没有任何依赖性。 因此,您可以享受传统的免费测试(至少一段时间;-)。
但是,当然有一条迁移路径可以使两个版本共存,并使您能够在使用JUnit 5编写新测试的同时维护现有的测试代码库。
相同但不同
但是,让我们最后来看一下JUnit 5测试的样子。 乍一看,变化不大。 一个简单的测试课程…
class FirstTests {@Testvoid firstTest() {fail();}
}
…与JUnit 4测试几乎没有区别。
但是,您发现细微的差别了吗? 是的,测试不再需要公开,但是如果您愿意,当然可以。
尽管注释仍用于标识设置和拆除测试环境的方法,但其名称已更改。 @BeforeClass/AfterClass
现在是@BeforeAll/AfterAll
而@Before/After
现在是@BeforeEach/AfterEach
。
使用@Disabled
批注仍然可以忽略测试。
@Test与@Test
如您所见,测试仍然使用@Test
注释标记。 但是要小心,如果您碰巧在类路径上也有JUnit 4。 JUnit 5带来了自己的@Test
批注,因此请确保导入正确的org.junit.gen5.api.Test
。 否则,JUnit 5测试运行程序将找不到您的测试。
需要注意的另一件事是新的@Test
注释不提供其他服务。 如果你曾经使用timeout
或expected
不时,你将需要更换他们的JUnit 5。
使用JUnit 5运行测试
难怪还没有IDE支持可以运行JUnit 5测试。 因此,我使用ConsoleRunner来执行实验。 以这种方式运行测试还需要三个模块:
-
org.junit:junit5-engine
-
org.junit:junit-launcher
-
org.junit:junit-console
我选择的IDE是Eclipse,为了从那里使用ConsoleRunner运行测试,我不得不手动扩展启动配置的Classpath 。 仅在添加包含已编译测试的test-classes
输出文件夹之后,它们才会被拾取。 但是,这种怪异也可能是由于我对Maven的了解不足,或者是由于Eclipse Maven集成中的特殊性。
JUnit 5团队还提供了基本的插件来执行Maven和Gradle构建中的测试。 如果您想尝试一下,请参阅“ 构建支持”一章。
断言
乍一看,断言没有太大变化,只是断言现在位于org.junit.gen5.api.Assertions
类中。
但是仔细观察发现, assertThat()
已消失,并且不幸的是,它依赖于Hamcrest。 这些方法实际上复制了MatcherAssert
提供的API,并将以前的JUnit版本绑定到Hamcrest库。 这种依赖性有时会导致类解析冲突。 特别是与其他库一起使用时,更糟糕的是,它们会自己包含Hamcrest的副本。
另一个更改是新的assertAll()
方法,该方法用于对断言进行分组。 例如
assertAll( "names", () -> {assertEquals( "John", person.getFirstName() );assertEquals( "Doe", person.getLastName() );
} );
将报告一个MultipleFailuresError
其中包含组中所有失败的断言。
然后,测试执行者有责任以适当的方式显示此故障。 但是,当前的ConsoleRunner实现尚未考虑分组故障,仅报告第一个故障:
Finished: testNames [junit5:com...GroupAssertionsTest#testNames()]=> Exception: names (1 failure)expected: <John> but was: <Mary>
我的第一个未经过滤的想法是,如果需要对断言进行分组,则可能是将代码分成多个测试的标志。 但是我还没有真正使用分组的断言,并且在某些地方也完全可以使用它们。
测试异常
测试异常已经统一。 要更换expected
和ExpectedException
现在有一个expectThrows
断言评估lambda表达式,并验证它抛出指定类型的异常。
例如,
@Test
void testException() {Foo foo = new Foo();Throwable exception = expectThrows( IllegalStateException.class, foo::bar );assertEquals( "some message", exception.getMessage() );
}
…如果调用foo::bar()
没有抛出IllegalStateException
将失败。 否则,将返回抛出的异常,并且可以对其进行进一步验证。 如果抛出的异常assertThrows()
,则还有一个assertThrows()
方法返回void。
再见亚军,规则和ClassRule
JUnit 5不再知道运行器,规则或类规则。 这些部分竞争的概念已由单个一致的扩展模型代替。
可以通过@ExtendWith
注释测试类或测试方法来声明性地使用扩展。 例如,希望使用模拟实例初始化某些字段的测试可以使用Mockito扩展,如下所示:
@ExtendWith(MockitoExtension.class)
class MockedTest {@MockPerson person;// ...}
如果您对这个主题感兴趣,请继续关注有关扩展以及如何将现有规则迁移到我计划编写的自定义扩展的单独文章。
测试方法参数
在JUnit 5中,现在允许方法具有参数。 这允许在方法级别注入依赖项。
为了提供参数,所谓的解析器是必需的,它是实现MethodParameterResolver
的扩展。 与所有其他扩展一样,要将解析器用于给定的方法或类,则需要使用@ExtendWith
进行声明。 还有两个不需要明确声明的内置解析器。 它们提供类型为TestInfo
和TestReporter
参数。
例如:
class MethodParametersTest {@Test// implicitly uses TestInfoParameterResolver to provide testInfovoid testWithBuiltIntParameterResolver( TestInfo testInfo ) {// ...}@Test@ExtendWith( CustomEnvironmentParameterResolver.class )// explicit resolver declared, could also be placed at class levelvoid testWithCustomParameterResolver( CustomEnvironment environment ) {// ...}
}
如果在运行时找不到匹配的参数解析器,则引擎将通过相应的消息使测试失败。
该文档指出,有计划提供其他扩展,其中还包括一个用于动态测试注册的扩展。 有了这个扩展,就有可能进行参数化测试。 考虑到测试方法已经接受了参数,参数化测试似乎也可以在方法级别上使用。
向后兼容
为了缩小差距,直到IDE本地支持JUnit 5为止,有一个JUnit 4 Runner能够执行为JUnit 5编写的测试。使用@RunWith(JUnit5.class)
批注来运行测试类和测试套件。
通过该运行器,可以并排运行JUnit 4和5测试类。 在单个测试中混合使用新旧概念当然是超出范围的,例如,将@Rule
与@ExtendWith
等共存。
Mockito和AssertJ等测试实用程序将继续使用新版本,而无需进行更改。 他们通过引发一个异常来与JUnit交互,即使在JUnit 5中,该异常仍然被认为是测试失败:)
JVM开放测试联盟
JUnit Lambda团队还为JVM建立了开放测试联盟 ,其目标是建立一个标准,以促进测试框架,断言库,模拟库,构建工具和IDE之间的交互。
主要目标是提供一个库,该库定义测试框架(例如JUnit,TestNG,Spock等)要使用的一组通用异常,以及断言库。 构建工具和IDE也将受益,因为它们可以依赖相同的类型集,而与测试框架无关。
可以以org.opentest4j
库的形式获得实现草案,您可以猜到它是JUnit 5使用的。
外表
我的印象是建立了新版本的基本概念。 诸如@Test,设置和拆卸注释之类的东西,单个扩展模型的概念可能仍将保持其当前形状。
但是许多细节似乎尚未解决,API可能会发生变化,我认为这在开发周期的现阶段是可以理解的。 API的每个部分都标有@API
注释 ,以指示其稳定性。
如果这篇文章引起了您的兴趣,并且您可能希望浏览文档以获取更多信息,那么还有很多值得探索的地方,例如:
- 用于过滤测试执行的标签
- 嵌套测试以对测试进行分组并表达测试组之间的关系
- 可扩展的思路进行测试验证规则 (如
@Test
和@BeforeEach
不应该在同样的方法进行) - 在运行时动态注册测试
- 注释有助于并行运行测试
第一个里程碑计划于2016年第一季度末发布。 此处提供了此发行版要解决的暂定项目列表。
翻译自: https://www.javacodegeeks.com/2016/02/junit-5-first-look-next-generation-junit.html