1. 概述
在这个简短的教程中,我们将讨论JUnit 5中的@Nested
注解。我们将首先通过一个简单的示例来理解嵌套测试类的工作方式,然后学习如何利用这个新特性处理更像生产环境的用例。
2. @Nested
如何工作
我们可以使用JUnit 5的@Nested
注解创建嵌套测试类。这个注解需要添加在每个包含测试的内部类的类级别上:
public class NestedTest {
@Nested
class FirstNestedClass {
@Test
void test() {
System.out.println("FirstNestedClass.test()");
}
}
@Nested
class SecondNestedClass {
@Test
void test() {
System.out.println("SecondNestedClass.test()");
}
}
}
因此,当我们运行父测试类时,所有嵌套测试类中的测试都会被执行。大多数IDE会以美观的层次结构显示测试:
此外,如果我们添加了设置或清理方法,它们会在声明时执行。例如,如果我们在三个类中分别为每个类添加一个@BeforeEach
方法,我们期望每个测试执行父类的设置方法,然后是它自己的类,最后执行测试本身:
public class NestedTest {
@BeforeEach()
void beforeEach() {
System.out.println("NestedTest.beforeEach()");
}
@Nested
class FirstNestedClass {
@BeforeEach()
void beforeEach() {
System.out.println("FirstNestedClass.beforeEach()");
}
@Test
void test() {
System.out.println("FirstNestedClass.test()");
}
}
@Nested
class SecondNestedClass {
@BeforeEach()
void beforeEach() {
System.out.println("SecondNestedClass.beforeEach()");
}
@Test
void test() {
System.out.println("SecondNestedClass.test()");
}
}
}
让我们运行测试类并检查控制台中打印语句的顺序:
正如预期的那样,两个测试都使用了由父类定义的通用设置。然后,它们运行自己类的设置方法,再执行测试。另外,其他设置和清理方法,如@BeforeAll
、@AfterEach
和@AfterAll
遵循相同的模式。
3. 何时使用@Nested
如果我们有一些测试的设置或清理方法在某些测试中重复,但不适用于所有测试,那么使用嵌套测试类非常有用。
此外,将测试集的设置用嵌套类表示,可以导致更具表达性的测试场景,并明确测试之间的关系。
3.1. 重用测试场景
让我们使用@Nested
注解为更复杂的情况编写一些测试。
假设我们在测试一个在线出版物的后端应用。该出版物的客户端可以有三种类型的会员资格:
public enum Membership {
FREE, SILVER, GOLD;
}
根据他们的会员资格,用户可以阅读或查看文章,或者将它们视为锁定状态。
首先,我们创建一个Publication
和三个Article
对象:
class OnlinePublicationTest {
private Publication publication;
@BeforeEach
void setupArticlesAndPublication() {
Article freeArticle = new Article("free article", Membership.FREE);
Article silverArticle = new Article("silver level article", Membership.SILVER);
Article goldArticle = new Article("gold level article", Membership.GOLD);
publication = new Publication(Arrays.asList(freeArticle, silverArticle, goldArticle));
}
@Test
void shouldHaveThreeArticlesInTotal() {
List<Article> allArticles = publication.getArticles();
assertThat(allArticles).hasSize(3);
}
}
现在,假设一个免费会员用户查看出版物。我们期望他只能阅读一篇,而其他两篇被视为“锁定”。让我们在一个嵌套测试类中分别写出这两个不同的单元测试:
@Nested
class UserWithoutMembership {
User freeFreya = new User("Freya", Membership.FREE);
@Test
void shouldOnlyReadFreeArticles() {
List<String> articles = publication.getReadableArticles(freeFreya);
assertThat(articles).containsExactly("free article");
}
@Test
void shouldSeeSilverAndGoldLevelArticlesAsLocked() {
List<String> articles = publication.getLockedArticles(freeFreya);
assertThat(articles).containsExactlyInAnyOrder("silver level article", "gold level article");
}
}
现在,为“银”级和“金”级会员用户运行类似的测试类,并运行整个类:
3.2. 使用带有@DisplayName
的嵌套测试
值得注意的是,类名和测试方法的名称一起描述了测试的整体场景。同样,我们可以在测试类和方法上使用@DisplayName
注解来进一步定制它们的显示方式。例如,我们可以使用“给定-当-然后”模式:
4. 总结
在这篇短文中,我们了解了JUnit 5的@Nested
注解的工作原理以及如何使用它创建具有表达力的测试场景。我们了解到,当多个测试的设置或清理相似,但并不适用于类中的所有测试时,我们可以创建一个嵌套测试类。
一如既往,本文的完整源代码可在GitHub上找到。