单元测试:立即使用 JUnit 5 的五大理由!
您肯定喜欢 JUnit。(我的意思是谁会不喜欢呢?)您或许已经听说 JUnit 5 ,但您不确定是否值得花时间了解它,对吧?在本文中,我将给出应该使用 JUnit 5 的五大理由。让我们马上开始阅读吧。
理由 #1 – JUnit 5 中出色的新特性
新注释
@Nested – 此注释允许在一个测试类中创建相关测试类的嵌套组。
使您能以一种更加自然的表达风格执行单元测试(比如行为驱动测试);
嵌套实现了一种额外的结构,使编写对多个重载方法的测试变得更容易(再见了,testMyMethod_StringStringInteger_1());
让所有嵌套测试共同分担类级别上的昂贵开销,同时在嵌套测试中仍然允许进行事前/事后测试(考虑 setUp()/tearDown())。
@Tag – 此注释允许为单元测试类和方法添加标签名称,使过滤机制能根据这些标签来跳过某个类或类中的测试(但不要将此概念与我们稍后将看到的前置条件 (Assumptions) 所提供的代码块级条件执行相混淆)。
Lambda 支持
选择断言 (Assertions) 和前置条件 (Assumptions) 并与 JDK 8 Lambda 支持相结合,您可以编写出更紧凑的测试。例如,新 assertAll() 方法允许对断言分组,执行某个分组中的所有断言并报告所有故障。使用 Assumptions 有条件地执行单元测试对于 JUnit 并不新奇,不过 Assumptions API 已经过改进,更具表达性。
新 assumingThat() 方法(包含在 Assumptions 类中)将这个更具表达性的新 API 与 lambda 表达式相结合,使创建的单元测试更容易理解,测试报告也更容易解释。
扩展
为了取代 JUnit 4 中的现有扩展点(比如 Runner 和 @Rule),JUnit 5 扩展模型在单元测试生命周期中定义了许多点(在编写本文时定义了 10 个点),并为每个点定义了一个对应接口。可使用 JUnit 5 扩展模型插入代码(您肯定已猜到它们称为扩展)来实现一个或多个这样的接口,在到达生命周期中的这个点时,就会执行您的代码。这使您能对代码执行事前和事后测试,甚至控制是否运行测试。
测试方法参数注入
标准 Runner 实现不允许测试方法有参数,除非您使用具有之前版本的 JUnit 的第三方库。这种情况在 JUnit 5 中已有所改变。通过 ParameterResolver 接口(对应于测试生命周期中的一个扩展点),可以通过编写代码在测试运行时解析参数。
ParameterResolver 接口通过 supports() 方法来判断您的扩展是否支持指定的参数。如果不支持,那么框架将继续执行;但是,如果支持,resolve() 方法会相应处理为该参数传入的对象实例的方法(创建/访问/删除)。确实非常有趣。
理由 #2 – JUnit 5 中的新架构
架构?啧啧啧。谁在乎?JUnit 5 中的架构在如何发现和运行测试与如何编写测试之间提供了明确的关注点分离。工具供应商使用 JUnit 内部 API 来完成日常工作(有讽刺意味的是,这限制了 JUnit 的发展能力)。
JUnit 5 架构被拆分为 3 个项目:
JUnit Jupiter
JUnit Vintage
JUnit Platform
这里的想法是,应用程序开发人员实际上并不需要担心 JUnit 如何完成其工作。但是,如果感到担心,同样的 API 也已公开(不再是仅有仅供工具开发人员使用的“内部”API),这提供了一种扩展 JUnit 的一致方法。
这意味着具有更好的工具支持和更干净的 API,从而支持多个 JUnit 测试版本。
理由 #3 – 对 JUnit 4 的向后兼容性
将软件升级到新的版本通常意味着需要重写所有代码,对吧?JUnit 5 不是这样;它完全向后兼容 JUnit 4。事实上,可以在运行 JUnit 5 测试的相同项目中运行 JUnit 4(甚至 JUnit 3)单元测试。需要投入少量工作(没有什么是不劳而获的,对吧?),但对 JUnit 3 和 4 的投资仍是值得的。
JUnit 5 如何实现此目的?通过 JUnit Vintage 测试引擎允许运行 JUnit 4 和 JUnit 3 测试。不仅如此,在实现 JUnit 5 原生工具支持之前,您应该仍能使用最喜爱的 IDE 使用 JUnitPlatform 类运行 JUnit4 测试。
诚然,JUnit 4 的一些特性与 JUnit 5 不太兼容(比如 Rules),但 JUnit 5 用户文档提供了迁移帮助。
理由 #4 – 开放测试联盟
开放……然后呢?为了推进一种开放式的 JUnit 工具支持方法,JUnit Lambda(JUnit 5 的代号)团队成立了一个名为 JVM 开放测试联盟的新开源项目。
目前,如果测试失败,测试框架有一个可以按下的按钮:java.lang.AssertionError。当然,它们都可以扩展该按钮,但每个框架都有自己的作用,这导致了一些分歧,使得将这些框架与我们最喜爱的 IDE 集成在一起变得很难。开放测试联盟的框架为所有测试框架提供了通用的构建块。
为什么要关注一致性问题?因为一致的实现能带来更有效的工具支持。而且这是所有开发人员都能很喜爱的一个有趣特性。
理由 #5 – 丰富的测试框架选择
正如 Nicolai Parlog 在这篇文章中解释的,JUnit 5 的总体改进目标是让 JUnit 成功地使用其他测试框架。因为可以根据 JUnit 5 架构来构建其他测试框架(通过提供测试引擎),所以我们拥有丰富的框架选择。
喜欢 TestNG?Spock?Coconut?或者您可能想编写自己的测试框架?只要该框架提供了必要的 JUnit 5 架构组件,而且您的 IDE 支持 JUnit 5,就可以使用该框架。想想这些特性有多酷。