Junit5使用指南

1. 概述

JUnit是Java生态系统中最流行的单元测试框架之一。JUnit 5版本包含许多激动人心的创新,其目标是支持Java 8及更高版本中的新功能,并支持许多不同的测试样式。

2. 依赖

2.1 Maven Dependencies

在 pom.xml 添加以下依赖项,需要注意的是 JUnit 5 需要 Java 8 以上才能运行。

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.1.0</version>
    <scope>test</scope>
</dependency>

3. 模块

3.1 JUnit Platform

该平台负责在JVM上启动测试框架。它在JUnit及其客户端(例如构建工具)之间定义了稳定而强大的接口。

最终目标是如何使其客户轻松地与JUnit集成在一起,以发现和执行测试。

它还定义了TestEngine API,用于开发在JUnit平台上运行的测试框架。这样,您可以通过实现自定义TestEngine将第三方测试库直接插入JUnit。

3.2 JUnit Jupiter

  • 该模块包括用于在JUnit 5中编写测试的新编程和扩展模型。与JUnit 4相比,新的注释为:
    • @TestFactory –表示作为动态测试的测试工厂的方法
    • @DisplayName –定义测试类或测试方法的自定义显示名称
    • @Nested –表示带注释的类是嵌套的非静态测试类
    • @Tag –声明用于过滤测试的标签
    • @ExtendWith –用于注册自定义扩展
    • @BeforeEach –表示带注释的方法将在每个测试方法之前执行(以前为@Before)
    • @AfterEach –表示带注释的方法将在每个测试方法之后执行(以前为@After)
    • @BeforeAll –表示带注释的方法将在当前类(以前为@BeforeClass)中的所有测试方法之前执行。
    • @AfterAll –表示带注释的方法将在当前类(以前为@AfterClass)中的所有测试方法之后执行。
    • @Disable –用于禁用测试类或方法(以前为@Ignore)

3.3 JUnit Vintage

支持在JUnit 5平台上运行基于JUnit 3和JUnit 4的测试。

4. 基本注释

为了讨论新的注释,我们将该部分分为以下几组,负责执行:测试之前,测试期间和测试之后:

4.1 @BeforeAll和@BeforeEach

下面是在主要测试用例之前要执行的简单代码的示例:

@BeforeAll
static void setup() {
    log.info("@BeforeAll - executes once before all test methods in this class");
}

@BeforeEach
void init() {
    log.info("@BeforeEach - executes before each test method in this class");
}

需要注意的重要一点是,带有@BeforeAll批注的方法必须是静态的,否则代码将无法编译。

4.2 @DisplayName和@Disabled

让我们转到新的可选测试方法:

@DisplayName("Single test successful")
@Test
void testSingleSuccessTest() {
    log.info("Success");
}

@Test
@Disabled("Not implemented yet")
void testShowSomething() {
}

如我们所见,我们可以使用新的注释来更改显示名称或禁用带有注释的方法。

@AfterEach和@AfterAll

最后,让我们讨论测试执行后连接到操作的方法:

@AfterEach
void tearDown() {
    log.info("@AfterEach - executed after each test method.");
}

@AfterAll
static void done() {
    log.info("@AfterAll - executed after all test methods.");
}

请注意,带有*@AfterAll的*方法也必须是静态方法。

5. 断言和假设

JUnit 5试图充分利用Java 8的新功能,特别是lambda表达式。

5.1 断言

断言已移至org.junit.jupiter.api.Assertions,并已得到显着改善。如前所述,您现在可以在断言中使用lambda:

@Test
void lambdaExpressions() {
    assertTrue(Stream.of(1, 2, 3)
      .stream()
      .mapToInt(i -> i)
      .sum() > 5, () -> "Sum should be greater than 5");
}

尽管上面的示例很简单,但是对断言消息使用lambda表达式的一个好处是可以对它进行延迟计算,如果消息的构建成本很高,则可以节省时间和资源。

现在还可以使用assertAll()对断言进行分组,这将使用MultipleFailuresError报告组内任何失败的断言:

 @Test
 void groupAssertions() {
     int[] numbers = {0, 1, 2, 3, 4};
     assertAll("numbers",
         () -> assertEquals(numbers[0], 1),
         () -> assertEquals(numbers[3], 3),
         () -> assertEquals(numbers[4], 1)
     );
 }

这意味着现在进行更复杂的断言更加安全,因为您将能够查明任何故障的确切位置。

5.2 假设条件

假设仅在满足某些条件时才用于运行测试。这通常用于测试正常运行所需的外部条件,但这些条件与所测试的内容没有直接关系。

您可以使用 assumeTrue(), assumeFalse()assumingThat() 来声明一个假设。

@Test
void trueAssumption() {
    assumeTrue(5 > 1);
    assertEquals(5 + 2, 7);
}

@Test
void falseAssumption() {
    assumeFalse(5 < 1);
    assertEquals(5 + 2, 7);
}

@Test
void assumptionThat() {
    String someString = "Just a string";
    assumingThat(
        someString.equals("Just a string"),
        () -> assertEquals(2 + 2, 4)
    );
}

如果假设失败,则抛出TestAbortedException,并且仅跳过测试。

假设也理解lambda表达式。

6. 异常测试

JUnit 5中有两种异常测试方法。这两种方法都可以通过使用*assertThrows()*方法来实现:

@Test
void shouldThrowException() {
    Throwable exception = assertThrows(UnsupportedOperationException.class, () -> {
      throw new UnsupportedOperationException("Not supported");
    });
    assertEquals(exception.getMessage(), "Not supported");
}

@Test
void assertThrowsException() {
    String str = null;
    assertThrows(IllegalArgumentException.class, () -> {
      Integer.valueOf(str);
    });
}

第一个示例用于验证引发的异常的更多详细信息,第二个示例仅用于验证异常的类型。

7. 测试套件

为了继续JUnit 5的新功能,我们将尝试了解在测试套件中聚合多个测试类的概念,以便我们可以一起运行它们。JUnit 5提供了两个注释:@SelectPackages和*@SelectClasses,*用于创建测试套件。

请记住,在此早期阶段,大多数IDE不支持这些功能。

让我们来看看第一个:

@RunWith(JUnitPlatform.class)
@SelectPackages("com.baeldung")
public class AllUnitTest {}

@SelectPackage用于指定运行测试套件时要选择的软件包的名称。在我们的示例中,它将运行所有测试。第二个注释*@SelectClasses*用于指定运行测试套件时要选择的类:

@RunWith(JUnitPlatform.class)
@SelectClasses({AssertionTest.class, AssumptionTest.class, ExceptionTest.class})
public class AllUnitTest {}

例如,上面的类将创建一个包含三个测试类的套件。请注意,这些类不必放在一个包中。

8. 动态测试

我们要介绍的最后一个主题是JUnit 5动态测试功能,该功能允许声明和运行在运行时生成的测试用例。与在编译时定义固定数量的测试用例的静态测试相反,动态测试允许我们在运行时动态定义测试用例。

动态测试可以通过带有*@TestFactory注释的工厂方法来生成。*让我们看一下代码示例:

@TestFactory
public Stream<DynamicTest> translateDynamicTestsFromStream() {
    return in.stream()
      .map(word ->
          DynamicTest.dynamicTest("Test translate " + word, () -> {
            int id = in.indexOf(word);
            assertEquals(out.get(id), translate(word));
          })
    );
}

这个例子非常简单易懂。我们想要使用分别命名为inout的两个ArrayList翻译单词。工厂方法必须返回StreamCollectionIterableIterator。在本例中,我们选择Java 8 Stream。

请注意,@ TestFactory方法不得为私有或静态。测试的数量是动态的,并且取决于ArrayList的大小。

9. 结论

本文是对JUnit 5即将进行的更改的快速概述。

我们可以看到,JUnit 5的体系结构发生了很大变化,这与平台启动器,与构建工具,IDE,其他单元测试框架的集成等有关。此外,JUnit 5与Java 8的集成程度更高,尤其是与Lambdas和Stream概念。

本文中使用的示例可以在GitHub项目中找到。

原文:https://www.baeldung.com/junit-5

参考连接

  • http://jsonassert.skyscreamer.org/
  • https://mkyong.com/java/jsonassert-how-to-unit-test-json-data/
  • https://www.baeldung.com/jsonassert
  • https://github.com/skyscreamer/JSONassert
  • https://www.baeldung.com/guide-to-jayway-jsonpath
  • https://www.baeldung.com/java-junit-hamcrest-guide
  • https://www.baeldung.com/integration-testing-a-rest-api