JUnit 5 @Nested 注释的目的是什么

What's the purpose of the JUnit 5 @Nested annotation

在 JUnit 5 中,有一个新的注解:@Nested

我明白注释是如何工作的,我明白为什么我们使用嵌套类, 我只是不明白为什么我们需要 嵌套测试 类.

@Nested 注释允许您拥有一个内部 class,它本质上是一个测试 class,允许您将多个测试 class 分组在同一个父项下(具有相同的初始化)。

我所有的测试都需要一个数据库服务器运行。我的大部分测试还需要数据库中的用户 table,才能登录。除此之外,一些测试需要好友 table,才能登录和查询好友。

每个资源都有设置和拆卸。我必须启动和停止服务器,创建和删除 tables.

使用 @Nested 注释,我可以将我的测试分组到嵌套的层次结构中 类,以便每个测试都获得层次结构中所有测试的设置和拆卸。

这种嵌套测试的想法在 Ruby 中得到了普及。 In Java 由 HierarchicalContextRunner 为 Junit 4 实现。请参阅其页面上的理由 https://github.com/bechte/junit-hierarchicalcontextrunner/wiki

I just don't understand why we need to have nested test class in our test.

@Nested 组织大型测试 class 确实有意义。

典型用例

很多时候,开发团队通过class定义一个测试class来测试。 这是一个共同的好习惯,但它也可能使您的测试 class 变得非常大,甚至有数百行。您确实可以使用 classes 进行测试,使用多种方法进行测试,每个方法都有多个场景,以及单元测试方法中测试场景所需的一些初始化步骤。
所有这些自然会增加测试class大小。
超过一个阈值(可能是 500 行左右),问问自己是否需要重构就变得合理了。

一个大 class(测试 class 与否),即使组织得很好,也比多个 classes 将具有高 cohesion/relationship 的东西分组更难阅读、维护.
在单元测试用例中,有时情况可能更糟,因为您可能找不到测试场景并在它存在时编写一个新场景,但由于测试 class 很大,您没有设法找到它。

@Nested : 解决方案

@Nested 通过提供在主(外部)测试 class.
的多个嵌套 class 中分组多个测试方法的可能性来解决这个问题 在主(外部)测试 class 中定义的所有嵌套 classes 的测试方法作为任何测试方法处理。所以 @BeforeEach@AfterEach@ExtendWith... 都应用了。
The single exception is @BeforeAll and @AfterAll :

Only non-static nested classes (i.e. inner classes) can serve as @Nested test classes. Nesting can be arbitrarily deep, and those inner classes are considered to be full members of the test class family with one exception: @BeforeAll and @AfterAll methods do not work by default. The reason is that Java does not allow static members in inner classes. However, this restriction can be circumvented by annotating a @Nested test class with @TestInstance(Lifecycle.PER_CLASS) (see Test Instance Lifecycle).

结合使用 @Nested 和采用 String 值的 @DisplayName 会变得更好,因为显示名称将用于 IDE 中的测试报告和构建工具并且可能包含空格、特殊字符,甚至表情符号。

示例

我有一个 FooService 有多种方法和多种场景。 我可以在单元测试 class.
的嵌套 classes 中对相同问题的场景进行分组 在这里,我选择测试方法来对它们进行分组(因此我按场景分组),但如果有意义的话,鉴别器可能是另一回事。

例如:

public class FooServiceTest {

    Foo foo;

    // invoked for ALL test methods
    @BeforeEach
    public void beforeEach() {
         Foo foo = new Foo(...);
    }

    @Nested
    @DisplayName("findWith methods")
    class FindMethods {
        @Test
        void findWith_when_X() throws Exception {
             //...
             foo.findWith(...);
             //...
        }
        @Test
        void findWith_when_Y() throws Exception {
             //...
             foo.findWith(...);
             //...

        }
        @Test
        void findWith_when_Z() throws Exception {
             //...
             foo.findWith(...);
             //...
        }           
    }

    @Nested
    @DisplayName("findAll methods")
    class FindAllMethods {
        @Test
        void findAll_when_X() throws Exception {
             //...
             foo.findAll(...);
             //...
        }
        @Test
        void findAll_when_Y() throws Exception {
             //...
             foo.findAll(...);
             //...

        }
        @Test
        void findAll_when_Z() throws Exception {
             //...
             foo.findAll(...);
             //...
        }   
    }   

    @Nested
    @DisplayName("computeBar methods")
    class ComputeBarMethods {   
         //...

    }

    @Nested
    @DisplayName("saveOrUpdate methods")
    class SaveOrUpdateMethods { 
         //...

    }
}

IDE中的示例效果图

嵌套的子方法默认折叠:

万一或测试失败或按需展开 Nesteds 的子方法:

@Nested - 主要从Junit5开始,提供我们正在尝试做的一个特性的延续逻辑。 将业务测试场景拆分成多个类,@nested正在使用中