为什么你应该开始使用 JUnit 5
显示名称
嵌套测试
扩展
从4到5的迁移
结论
本文转载自我的博客,您可以在这里找到原文。
什么是 JUnit 5?
根据其网站信息,
JUnit 5 是 JUnit 的下一代版本。其目标是为 JVM 上的开发者端测试创建一个最新的基础架构。这包括重点支持 Java 8 及更高版本,并支持多种不同的测试风格。
本文面向所有听说过或没听说过 JUnit 5,并想知道为什么要使用它的人。
我不想详细介绍所有功能或其内部工作原理,我只想展示一些目前对我来说最有价值的新功能。
那么,让我们直接进入正题……
显示名称
在之前的 JUnit 中,测试用例的名称来源于它们的测试方法。
例如:
public class AccountTest {
@Test
public void testMethod() {
// do some tests here
}
}
运行后,它会输出testMethod测试名称,但这可能没什么用,更重要的是,它难以阅读。测试应该易于理解,因为测试是唯一不会被弃用的真正代码文档。因此,人们开始尝试提出一些更具描述性的测试名称。
public class AccountTest {
@Test
public void withdrawSomeAmountGreaterThanCurrentBalanceFailsWithException() {
// do some tests here
}
}
好些了吗?倒也未必。至少它能让你大致了解接下来会发生什么。大概就是从账户中提取超过当前余额的金额。这应该会失败。但如何让它更易读呢?一些开发者(包括我自己)开始打破 Java 严格的驼峰命名约定,转而使用下划线来分隔测试的不同部分,例如method__scenario__expectedResult:
public class AccountTest {
@Test
public void withdraw__amountGreaterThanCurrentBalance__failsWithException() {
// do some tests here
}
}
虽然这种写法更易读一些,但对于习惯驼峰命名法的Java开发者来说,读起来会很别扭。你真正想要的是使用任意字符串作为测试名称。
Kotlin 允许你使用字符串字面量,例如:
internal class AccountTest {
@Test
fun `should thrown an exception when an amount is withdrawn greater that the current balance`() {
// do some tests here
}
JUnit 5 来帮忙了。虽然不如 Kotlin 那么简单,但 JUnit 5 提供了一个@DisplayName注解,可以用于测试类和方法。缺点是,你仍然需要为方法命名:
@DisplayName("An account")
class AccountTest {
@Test
@DisplayName("should throw an exception when an amount is withdrawn greater that the current balance")
void withdrawBalanceExceeded() {
// do some tests here
}
}
所以,尽管出现了典型的 Java 注解混乱,但结果(如 IDE 显示的内容)却更易于阅读。
请注意,您不再需要使用public方法和类。
嵌套测试
在和同事结对编程时,我经常复制粘贴测试用例和描述,然后只修改其中的一部分来测试不同的边界情况。她建议我使用嵌套测试。我们尝试了一下,我立刻就爱上了这种方法。
以前我写的测试用例是这样的:
- 账户
- 当提取金额超过当前余额时,应抛出异常。
- 余额应减少提取金额。
你可以做类似的事情。
- 账户
- 退出
- 当金额大于当前余额时,应抛出异常。
- 应将余额减少该金额
- 退出
使用 JUnit 5 时,它看起来像这样:
@DisplayName("An account")
public class AccountTest {
@Nested
@DisplayName("withdrawal")
class Withdrawal {
@Test
@DisplayName("should throw an exception when the amount greater that the current balance")
public void failureBalanceExceeded() {
// do some tests here
}
@Test
@DisplayName("should reduce the balance by the amount")
public void success() {
// do some tests here
}
}
}
同样,由于需要将测试包装在嵌套类中,因此代码行数会增加,@Nested但这会使测试本身更加简洁。
扩展
@ExtendWithJUnit 5 的设计理念是高度可扩展。可以通过在类级别使用注解来添加自定义或第三方扩展。
Spring Boot
例如,Spring Boot 集成测试现在可以通过以下方式运行@ExtendWith(SpringExtension.class)。请注意,这仅适用于 Spring Boot 2.x 及更高版本。
Mockito 扩展
将会有一个更新版本MockitoExtension,可以实现类似的功能:
@ExtendWith(MockitoExtension.class)
class MyMockitoTest {
@BeforeEach
void init(@Mock Person person) {
when(person.getName()).thenReturn("Dilbert");
}
@Test
void simpleTestWithInjectedMock(@Mock Person person) {
assertEquals("Dilbert", person.getName());
}
}
所以你既不需要编写Person person = mock(Person.class)也不需要使用@Mock private Person person。
从4到5的迁移
JUnit 5 的这些新特性是有代价的:它们与 JUnit 4 测试不兼容。这被称为 JUnit Jupiter。
“等等,那我现有的 500 个测试用例怎么办?”你可能会问。其实情况并没有你想的那么糟糕。JUnit 团队做了一件很聪明的事,他们把所有 JUnit Jupiter 的代码和注解都移到了一个单独的包里,这样就可以在同一个代码库里同时使用 JUnit 4 和 JUnit 5 了。你仍然需要添加新的 JUnit 平台,JUnit 4 的测试用例被称为 JUnit Vintage。这里有一篇关于迁移的不错文章,你可以在这里找到。
我们有一个包含数百个测试的项目,我们正在逐步迁移它们。所有新测试都使用 JUnit 5 编写,一旦需要修改现有测试,我们就进行迁移。这基本上会删除所有 JUnit测试,并用新版本和旧版本import进行替换。@Before@BeforeEach@BeforeClass@BeforeAll
结论
虽然 Java 本身可以通过引入元编程(元编程也有其缺点)或至少允许使用字符串字面量作为方法名来使测试更具可读性,从而得到很大的改进,但 JUnit 5 比 JUnit 4 要好得多,而且我认为将来会有很多扩展功能推出。
此外,JUnit 的断言库也有很多改进,但由于我使用的是AssertJ,所以没有涉及这部分内容。详情请参阅 JUnit 5 文档。
文章来源:https://dev.to/stealthmusic/why-you-should-start-using-junit-5-5gk2