导航

JUnit 5 大杂烩

Posted on 2017-07-10 21:12  Young哥哥  阅读(778)  评论(0)    收藏  举报

本文档的目的是为编写测试,扩展作者和引擎作者的程序员以及构建工具和IDE供应商提供全面的参考文档。

1.1。什么是JUnit 5?

与以前版本的JUnit不同,JUnit 5由三个不同子项目的几个不同模块组成。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit的平台可作为一个基础发射测试框架在JVM上。它还定义了TestEngine开发在平台上运行的测试框架API。此外,该平台还提供了一个 控制台启动器,从命令行启动平台,并为Gradle和 Maven构建插件以及一个 基于JUnit 4的Runner,用于TestEngine 在平台上运行任何操作。

JUnit Jupiter用于在JUnit 5中编写测试和扩展的新编程模型和 扩展模型的组合.Jupiter子项目提供了一个TestEngine在平台上运行基于Jupiter的测试。

JUnit Vintage provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform.

1.2。支持的Java版本

JUnit 5在运行时需要Java 8(或更高版本)。但是,您仍然可以测试使用以前版本的JDK编译的代码。

 

2.安装

最终版本和里程碑的工作部署到Maven Central。

Snapshot工件部署到Sonatype的/ org / junit下 快照库

2.1。依赖元数据

2.1.1。JUnit平台

  • 组IDorg.junit.platform

  • 版本1.0.0-M5

  • 神器ID

    junit-platform-commons

    JUnit的内部公用库/实用程序。这些实用程序仅用于JUnit框架本身中的使用。不支持外部用户的任何用途。使用自己的风险!

    junit-platform-console

    支持从控制台发现和执行JUnit平台上的测试。有关详细信息,请参阅 控制台启动器

    junit-platform-console-standalone

    在Maven Central中,在junit-platform-console-standalone 目录下提供了包含所有依赖项的可执行JAR 有关详细信息,请参阅控制台启动器

    junit-platform-engine

    用于测试引擎的公共API 有关详细信息,请参阅插入自己的测试引擎

    junit-platform-gradle-plugin

    支持使用Gradle在JUnit平台上发现和执行测试 

    junit-platform-launcher

    用于配置和启动测试计划的公共API - 通常由IDE和构建工具使用。有关详细信息,请参阅JUnit Platform Launcher API

    junit-platform-runner

    用于在JUnit 4环境中的JUnit平台上执行测试和测试套件的运行程序。有关详细信息,请参阅使用JUnit 4运行JUnit平台

    junit-platform-suite-api

    在JUnit平台上配置测试套件的注释。JUnitPlatform转换器支持 ,可能由第三方 TestEngine实现。

    junit-platform-surefire-provider

    支持使用Maven Surefire在JUnit平台上发现和执行测试 

2.1.2。JUnit木星

  • 组IDorg.junit.jupiter

  • 版本5.0.0-M5

  • 神器ID

    junit-jupiter-api

    用于编写测试扩展的 JUnit Jupiter API 

    junit-jupiter-engine

    JUnit Jupiter测试引擎实现,只在运行时需要。

    junit-jupiter-params

    支持JUnit Jupiter中的参数化测试

    junit-jupiter-migrationsupport

    从JUnit 4到JUnit Jupiter的迁移支持,只需要运行所选的JUnit 4规则。

2.1.3。JUnit复古

  • 组IDorg.junit.vintage

  • 版本4.12.0-M5

  • 神器ID

    junit-vintage-engine

    JUnit复古测试引擎实现,允许在新的JUnit平台上运行复制JUnit测试,即以JUnit 3或JUnit 4样式编写的测试。

2.2。依赖关系图

组件图

2.3。JUnit木星示例项目

junit5-samples信息库托管基于JUnit的木星和JUnit复古样本项目的集合。你会找到相应的build.gradlepom.xml下面的项目。

写作测试

第一个测试用例
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

class FirstJUnit5Tests {

    @Test
    void myFirstTest() {
        assertEquals(2, 1 + 1);
    }

}

3.1。注释

JUnit Jupiter支持以下注释来配置测试和扩展框架。

所有核心注释位于 模块中的org.junit.jupiter.api包中junit-jupiter-api

注解描述

@Test

表示一种方法是一种测试方法。与JUnit 4的@Test注释不同,此注释不声明任何属性,因为JUnit Jupiter中的测试扩展基于自己的专用注释进行操作。

@RepeatedTest

表示方法是重复测试的测试模板

@TestFactory

表示一种方法是动态测试的测试工厂

@TestInstance

用于为注释测试类配置测试实例生命周期

@TestTemplate

表示一种方法是用于被设计为多次调用的测试用例模板,具体取决于注册提供者返回的调用上下文的数量

@DisplayName

为测试类或测试方法声明自定义显示名称

@BeforeEach

表示应该执行注释方法 before each @Test@RepeatedTest@ParameterizedTest, or @TestFactory method in the current class; analogous to JUnit 4’s @Before. Such methods are inherited.

@AfterEach

表示该注释的方法应该被执行之后 每个 @Test@RepeatedTest@ParameterizedTest,或@TestFactory方法在当前类; 类似于JUnit 4 @After这样的方法是继承的

@BeforeAll

表示该注释的方法应该被执行之前 所有 @Test@RepeatedTest@ParameterizedTest,和@TestFactory方法在当前类; 类似于JUnit 4 @BeforeClass这样的方法是继承的,必须static除非测试类被注释@TestInstance(Lifecycle.PER_CLASS)

@AfterAll

表示该注释的方法应该被执行之后 的所有 @Test@RepeatedTest@ParameterizedTest,和@TestFactory方法在当前类; 类似于JUnit 4 @AfterClass这样的方法是继承的,必须static除非测试类被注释@TestInstance(Lifecycle.PER_CLASS)

@Nested

表示注释类是嵌套的非静态测试类。@BeforeAll并且@AfterAll方法不能在@Nested测试类中使用,除非它被注释@TestInstance(Lifecycle.PER_CLASS)

@Tag

用于在类或方法级别声明过滤测试的标签 ; 类似于TestNG中的测试组或JUnit 4中的类别

@Disabled

用于禁用测试类或测试方法; 类似于JUnit 4@Ignore

@ExtendWith

用于注册自定义扩展

3.1.1。元注释和组合注释

JUnit Jupiter注释可以用作元注释这意味着您可以定义自己编写的注释,它将自动继承其元注释的语义。

例如,您可以@Tag("fast")在代码库中复制和粘贴代码(请参阅 标记和过滤),您可以创建 如下命名的自定义组合注释@Fast@Fast然后可以作为替代替代@Tag("fast")

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.api.Tag;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
public @interface Fast {
}

3.2。标准测试类

一个标准测试用例
import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class StandardTests {

    @BeforeAll
    static void initAll() {
    }

    @BeforeEach
    void init() {
    }

    @Test
    void succeedingTest() {
    }

    @Test
    void failingTest() {
        fail("a failing test");
    }

    @Test
    @Disabled("for demonstration purposes")
    void skippedTest() {
        // not executed
    }

    @AfterEach
    void tearDown() {
    }

    @AfterAll
    static void tearDownAll() {
    }

}
  测试类和测试方法都不需要public

3.3。显示名称

测试类和测试方法可以声明由测试运行者和测试报告显示的自定义显示名称 - 空格,特殊字符,甚至emojis。

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("A special test case")
class DisplayNameDemo {

    @Test
    @DisplayName("Custom test name containing spaces")
    void testWithDisplayNameContainingSpaces() {
    }

    @Test
    @DisplayName("╯°□°)╯")
    void testWithDisplayNameContainingSpecialCharacters() {
    }

    @Test
    @DisplayName("😱")
    void testWithDisplayNameContainingEmoji() {
    }

}

3.4。断言

JUnit Jupiter带有许多JUnit 4的断言方法,并添加了一些可以很好地与Java 8 lambdas一起使用的方法。所有JUnit Jupiter断言都是中的static方法org.junit.jupiter.Assertions

import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

class AssertionsDemo {

    @Test
    void standardAssertions() {
        assertEquals(2, 2);
        assertEquals(4, 4, "The optional assertion message is now the last parameter.");
        assertTrue(2 == 2, () -> "Assertion messages can be lazily evaluated -- "
                + "to avoid constructing complex messages unnecessarily.");
    }

    @Test
    void groupedAssertions() {
        // In a grouped assertion all assertions are executed, and any
        // failures will be reported together.
        assertAll("person",
            () -> assertEquals("John", person.getFirstName()),
            () -> assertEquals("Doe", person.getLastName())
        );
    }

    @Test
    void dependentAssertions() {
        // Within a code block, if an assertion fails the
        // subsequent code in the same block will be skipped.
        assertAll("properties",
            () -> {
                String firstName = person.getFirstName();
                assertNotNull(firstName);

                // Executed only if the previous assertion is valid.
                assertAll("first name",
                    () -> assertTrue(firstName.startsWith("J")),
                    () -> assertTrue(firstName.endsWith("n"))
                );
            },
            () -> {
                // Grouped assertion, so processed independently
                // of results of first name assertions.
                String lastName = person.getLastName();
                assertNotNull(lastName);

                // Executed only if the previous assertion is valid.
                assertAll("last name",
                    () -> assertTrue(lastName.startsWith("D")),
                    () -> assertTrue(lastName.endsWith("e"))
                );
            }
        );
    }

    @Test
    void exceptionTesting() {
        Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
            throw new IllegalArgumentException("a message");
        });
        assertEquals("a message", exception.getMessage());
    }

    @Test
    void timeoutNotExceeded() {
        // The following assertion succeeds.
        assertTimeout(ofMinutes(2), () -> {
            // Perform task that takes less than 2 minutes.
        });
    }

    @Test
    void timeoutNotExceededWithResult() {
        // The following assertion succeeds, and returns the supplied object.
        String actualResult = assertTimeout(ofMinutes(2), () -> {
            return "a result";
        });
        assertEquals("a result", actualResult);
    }

    @Test
    void timeoutNotExceededWithMethod() {
        // The following assertion invokes a method reference and returns an object.
        String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);
        assertEquals("hello world!", actualGreeting);
    }

    @Test
    void timeoutExceeded() {
        // The following assertion fails with an error message similar to:
        // execution exceeded timeout of 10 ms by 91 ms
        assertTimeout(ofMillis(10), () -> {
            // Simulate task that takes more than 10 ms.
            Thread.sleep(100);
        });
    }

    @Test
    void timeoutExceededWithPreemptiveTermination() {
        // The following assertion fails with an error message similar to:
        // execution timed out after 10 ms
        assertTimeoutPreemptively(ofMillis(10), () -> {
            // Simulate task that takes more than 10 ms.
            Thread.sleep(100);
        });
    }

    private static String greeting() {
        return "hello world!";
    }

}

3.4.1。第三方断言库

即使由JUnit Jupiter提供的断言设施对于许多测试场景也是足够的,但有时候需要或需要更多的功能和附加功能,如 匹配器在这种情况下,JUnit团队建议使用AssertJHamcrestTruth第三方声明库。因此,开发人员可以自由使用他们选择的断言库。

例如,匹配器和流畅的API 的组合可用于使断言更具描述性和可读性。但是,JUnit Jupiter的org.junit.jupiter.Assertions类不提供 assertThat() 类似于org.junit.Assert接受Hamcrest的JUnit 4 类中的方法 Matcher相反,鼓励开发人员使用第三方断言库提供的匹配器的内置支持。

以下示例演示了如何assertThat()在JUnit Jupiter测试中使用Hamcrest 支持。只要Hamcrest库已经添加到classpath,你可以静态导入的方法,如assertThat()is()equalTo(),然后用它们在测试,如在assertWithHamcrestMatcher()下面的方法。

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

import org.junit.jupiter.api.Test;

class HamcrestAssertionDemo {

    @Test
    void assertWithHamcrestMatcher() {
        assertThat(2 + 1, is(equalTo(3)));
    }

}

当然,基于JUnit 4编程模型的遗留测试可以继续使用 org.junit.Assert#assertThat

3.5。假设

JUnit Jupiter带有JUnit 4提供的假设方法的一部分,并添加了一些适用于Java 8 lambdas的自定义方法。所有JUnit Jupiter假设都是org.junit.jupiter.Assumptions中的静态方法

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;

import org.junit.jupiter.api.Test;

class AssumptionsDemo {

    @Test
    void testOnlyOnCiServer() {
        assumeTrue("CI".equals(System.getenv("ENV")));
        // remainder of test
    }

    @Test
    void testOnlyOnDeveloperWorkstation() {
        assumeTrue("DEV".equals(System.getenv("ENV")),
            () -> "Aborting test: not on developer workstation");
        // remainder of test
    }

    @Test
    void testInAllEnvironments() {
        assumingThat("CI".equals(System.getenv("ENV")),
            () -> {
                // perform these assertions only on the CI server
                assertEquals(2, 2);
            });

        // perform these assertions in all environments
        assertEquals("a string", "a string");
    }

}

3.6。禁用测试

这是一个禁用的测试用例。

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

@Disabled
class DisabledClassDemo {
    @Test
    void testWillBeSkipped() {
    }
}

这是一个使用禁用测试方法的测试用例。

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class DisabledTestsDemo {

    @Disabled
    @Test
    void testWillBeSkipped() {
    }

    @Test
    void testWillBeExecuted() {
    }
}

3.7。标记和过滤

测试类和方法可以被标记。稍后可以使用这些标签过滤 测试发现和执行

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("fast")
@Tag("model")
class TaggingDemo {

    @Test
    @Tag("taxes")
    void testingTaxCalculation() {
    }

}

3.8。测试实例生命周期

为了允许单独执行单独的测试方法,并且为了避免由于可变测试实例状态引起的意外的副作用,JUnit在执行每个测试方法之前创建每个测试类的新实例(参见下面的注释,作为测试方法的资格))。这个“每个方法”的测试实例生命周期是JUnit Jupiter中的默认行为,类似于所有先前版本的JUnit。

如果您希望JUnit Jupiter在同一个测试实例上执行所有测试方法,只需用你的测试类来注释@TestInstance(Lifecycle.PER_CLASS)使用此模式时,每个测试类将创建一个新的测试实例。因此,如果您的测试方法依赖于存储在实例变量中的状态,则可能需要重置该状态@BeforeEach@AfterEach方法。

“每类”模式比默认的“每方法”模式有一些额外的好处。具体来说,使用“每类”模式,可以声明@BeforeAll和 @AfterAll非静态方法以及接口default方法。因此,“每类”模式也使得可以 在测试类中使用@BeforeAll@AfterAll方法@Nested

如果您正在使用Kotlin编程语言编写测试,那么您也可以通过切换到“每类”测试实例生命周期模式来更容易地实现@BeforeAll实现@AfterAll方法。

  在试验实例的上下文生命周期测试的方法是用任何注释的方法@Test@RepeatedTest@ParameterizedTest@TestFactory, or @TestTemplate.

3.9。嵌套测试

嵌套测试使测试作者能够表达几组测试之间的关系。这是一个精心设计的例子。

用于测试堆栈的嵌套测试套件
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.EmptyStackException;
import java.util.Stack;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

@DisplayName("A stack")
class TestingAStackDemo {

    Stack<Object> stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, () -> stack.pop());
        }

        @Test
        @DisplayName("throws EmptyStackException when peeked")
        void throwsExceptionWhenPeeked() {
            assertThrows(EmptyStackException.class, () -> stack.peek());
        }

        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }

            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() {
                assertEquals(anElement, stack.pop());
                assertTrue(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when peeked but remains not empty")
            void returnElementWhenPeeked() {
                assertEquals(anElement, stack.peek());
                assertFalse(stack.isEmpty());
            }
        }
    }
}
  只有非静态嵌套类(即内部类)可以作为@Nested测试类。嵌套可以是任意深度的,并且那些内部类被认为是测试类的全部成员,但有一个例外:默认情况下@BeforeAll@AfterAll 方法不起作用原因是Java不允许内部类的成员。但是,通过 使用(参见 测试实例生命周期注释测试类,可以规避此限制static@Nested@TestInstance(Lifecycle.PER_CLASS)

3.10。构造函数和方法的依赖注入

在所有先前的JUnit版本中,测试构造函数或方法都不允许有参数(至少不符合标准Runner实现)。作为JUnit Jupiter的主要变化之一,现在允许测试构造函数和方法都有参数。这允许更大的灵活性,并为构造函数和方法启用依赖注入

ParameterResolver定义了希望 在运行时动态解析参数的测试扩展的API 如果测试构造函数或@Test@TestFactory, @BeforeEach@AfterEach@BeforeAll,或@AfterAll方法接受一个参数,该参数必须在运行时由注册解决ParameterResolver

目前有三个自动注册的内置解析器。

  • TestInfoParameterResolver:如果一个方法参数是类型TestInfoTestInfoParameterResolver将提供TestInfo与当前测试相对应的实例作为参数的值。所述TestInfo然后可以被用于检索有关当前测试信息,如测试的显示名称,测试类,测试方法,或相关联的标签。显示名称是技术名称,例如测试类或测试方法的名称,或通过配置的自定义名称@DisplayName

    TestInfo作为TestNameJUnit 4中规则的替代替代。以下演示了如何TestInfo注入到测试构造函数, @BeforeEach方法和@Test方法中。

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;

@DisplayName("TestInfo Demo")
class TestInfoDemo {

    TestInfoDemo(TestInfo testInfo) {
        assertEquals("TestInfo Demo", testInfo.getDisplayName());
    }

    @BeforeEach
    void init(TestInfo testInfo) {
        String displayName = testInfo.getDisplayName();
        assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
    }

    @Test
    @DisplayName("TEST 1")
    @Tag("my tag")
    void test1(TestInfo testInfo) {
        assertEquals("TEST 1", testInfo.getDisplayName());
        assertTrue(testInfo.getTags().contains("my tag"));
    }

    @Test
    void test2() {
    }

}
  • RepetitionInfoParameterResolver:如果在一个方法参数@RepeatedTest, @BeforeEach@AfterEach方法的类型是RepetitionInfo时, RepetitionInfoParameterResolver将提供的一个实例RepetitionInfo。 RepetitionInfo然后可以用于检索关于当前重复的信息和对应的重复的总数@RepeatedTest但是请注意,这RepetitionInfoParameterResolver不是在a的上下文之外注册的@RepeatedTest参见重复测试示例

  • TestReporterParameterResolver:如果一个方法参数是类型TestReporterTestReporterParameterResolver将提供一个实例TestReporterTestReporter可用于发布关于当前试运行的附加数据。数据可以TestExecutionListener.reportingEntryPublished()通过IDE 进行使用,也可以被IDE查看或包含在报告中。

    在JUnit Jupiter中,您应该使用TestReporter用于在JUnit 4 stdout其中打印信息的位置。stderr使用@RunWith(JUnitPlatform.class)甚至可以输出所有报告的条目stdout

import java.util.HashMap;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestReporter;

class TestReporterDemo {

    @Test
    void reportSingleValue(TestReporter testReporter) {
        testReporter.publishEntry("a key", "a value");
    }

    @Test
    void reportSeveralValues(TestReporter testReporter) {
        HashMap<String, String> values = new HashMap<>();
        values.put("user name", "dk38");
        values.put("award year", "1974");

        testReporter.publishEntry(values);
    }

}
  其他参数解算装置必须通过注册相应的明确启用 扩展通过@ExtendWith

查看MockitoExtension一个自定义的例子ParameterResolver虽然不是要生产就绪,但它展示了扩展模型和参数解析过程的简单性和表现力。 MyMockitoTest演示如何注入Mockito模拟@BeforeEach@Test 方法。

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import com.example.Person;
import com.example.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());
    }

}

3.11。测试接口和默认方法

JUnit的木星允许@Test@RepeatedTest@ParameterizedTest@TestFactory@TestTemplate@BeforeEach,和@AfterEach对接口中声明default 的方法。如果具体的测试类被注释(见 测试实例生命周期@BeforeAll@AfterAll可以static在测试接口中的default方法或接口方法声明这里有些例子。@TestInstance(Lifecycle.PER_CLASS)

interface TestLifecycleLogger {

    static final Logger LOG = Logger.getLogger(TestLifecycleLogger.class.getName());

    @BeforeAll
    static void beforeAllTests() {
        LOG.info("Before all tests");
    }

    @AfterAll
    static void afterAllTests() {
        LOG.info("After all tests");
    }

    @BeforeEach
    default void beforeEachTest(TestInfo testInfo) {
        LOG.info(() -> String.format("About to execute [%s]",
            testInfo.getDisplayName()));
    }

    @AfterEach
    default void afterEachTest(TestInfo testInfo) {
        LOG.info(() -> String.format("Finished executing [%s]",
            testInfo.getDisplayName()));
    }

}
interface TestInterfaceDynamicTestsDemo {

    @TestFactory
    default Collection<DynamicTest> dynamicTestsFromCollection() {
        return Arrays.asList(
            dynamicTest("1st dynamic test in test interface", () -> assertTrue(true)),
            dynamicTest("2nd dynamic test in test interface", () -> assertEquals(4, 2 * 2))
        );
    }

}

@ExtendWith并且@Tag可以在测试界面上声明,以便实现接口的类自动继承其标签和扩展名。请参阅TimingExtension的源代码 测试执行回调之前和之后

@Tag("timed")
@ExtendWith(TimingExtension.class)
interface TimeExecutionLogger {
}

在测试类中,您可以实现这些测试接口来应用它们。

class TestInterfaceDemo implements TestLifecycleLogger,
        TimeExecutionLogger, TestInterfaceDynamicTestsDemo {

    @Test
    void isEqualValue() {
        assertEquals(1, 1, "is always equal");
    }

}

运行TestInterfaceDemo输出结果类似于以下内容:

:junitPlatformTest 
INFO example.TestLifecycleLogger  - 所有测试之前
INFO example.TestLifecycleLogger  - 关于执行[dynamicTestsFromCollection()] 
INFO example.TimingExtension  - 方法[dynamicTestsFromCollection]花了13 ms。
INFO example.TestLifecycleLogger  - 完成执行[dynamicTestsFromCollection()] 
INFO example.TestLifecycleLogger  - 关于执行[isEqualValue()] 
INFO example.TimingExtension  - 方法[isEqualValue]花费1 ms。
INFO example.TestLifecycleLogger  - 完成执行[isEqualValue()] 
INFO示例。

Another possible application of this feature is to write tests for interface contracts. For example, you can write tests for how implementations of Object.equals or Comparable.compareToshould behave as follows.

public interface Testable<T> {

    T createValue();

}
public interface EqualsContract<T> extends Testable<T> {

    T createNotEqualValue();

    @Test
    default void valueEqualsItself() {
        T value = createValue();
        assertEquals(value, value);
    }

    @Test
    default void valueDoesNotEqualNull() {
        T value = createValue();
        assertFalse(value.equals(null));
    }

    @Test
    default void valueDoesNotEqualDifferentValue() {
        T value = createValue();
        T differentValue = createNotEqualValue();
        assertNotEquals(value, differentValue);
        assertNotEquals(differentValue, value);
    }

}
public interface ComparableContract<T extends Comparable<T>> extends Testable<T> {

    T createSmallerValue();

    @Test
    default void returnsZeroWhenComparedToItself() {
        T value = createValue();
        assertEquals(0, value.compareTo(value));
    }

    @Test
    default void returnsPositiveNumberComparedToSmallerValue() {
        T value = createValue();
        T smallerValue = createSmallerValue();
        assertTrue(value.compareTo(smallerValue) > 0);
    }

    @Test
    default void returnsNegativeNumberComparedToSmallerValue() {
        T value = createValue();
        T smallerValue = createSmallerValue();
        assertTrue(smallerValue.compareTo(value) < 0);
    }

}

在测试类中,您可以实现两个合同接口,从而继承相应的测试。当然,你必须实现抽象方法。

class StringTests implements ComparableContract<String>, EqualsContract<String> {

    @Override
    public String createValue() {
        return "foo";
    }

    @Override
    public String createSmallerValue() {
        return "bar"; // 'b' < 'f' in "foo"
    }

    @Override
    public String createNotEqualValue() {
        return "baz";
    }

}
  上述测试仅仅是作为例子而不是完整的。

3.12。重复测试

JUnit Jupiter提供了通过使用@RepeatedTest指定方法和指定所需重复总数来简单地重复测试指定次数的功能重复测试的每次调用的行为就像一个常规@Test方法的执行, 完全支持相同的生命周期回调和扩展。

以下示例演示如何声明一个名为的测试repeatedTest()将自动重复10次。

@RepeatedTest(10)
void repeatedTest() {
    // ...
}

除了指定重复次数之外,还可以通过 注释name属性为每个重复配置自定义显示名称@RepeatedTest此外,显示名称可以是由静态文本和动态占位符的组合组成的图案。目前支持以下占位符。

  • {displayName}:显示@RepeatedTest方法名称

  • {currentRepetition}:当前重复计数

  • {totalRepetitions}:重复的总数

基于以下模式生成给定重复的默认显示名称:"repetition {currentRepetition} of {totalRepetitions}"因此,上一个repeatedTest()示例的个别重复的显示名称将是:repetition 1 of 10repetition 2 of 10等等。如果您希望@RepeatedTest每个重复名称中包含方法的显示名称,您可以定义自己的自定义模式或使用预定义RepeatedTest.LONG_DISPLAY_NAME模式。后者是等于"{displayName} :: repetition {currentRepetition} of {totalRepetitions}"其导致个别重复像显示名称 repeatedTest() :: repetition 1 of 10repeatedTest() :: repetition 2 of 10等等。

为了检索有关当前重复和重复编程的总数的信息,一个开发者可以选择具有的实例 RepetitionInfo注入@RepeatedTest@BeforeEach@AfterEach方法。

3.12.1。重复测试示例

RepeatedTestsDemo本节末尾课程演示了重复测试的几个示例。

repeatedTest()方法与上一节的示例相同; 而, repeatedTestWithRepetitionInfo()演示如何使一个RepetitionInfo注入测试的实例 访问当前重复测试的重复次数。

接下来的两种方法演示了如何在每个重复的显示名称中包含@DisplayName该 @RepeatedTest方法的自定义customDisplayName() 将自定义显示名称与自定义模式相结合,然后用于TestInfo验证生成的显示名称的格式。Repeat!{displayName}它来源于@DisplayName声明,1/1来自{currentRepetition}/{totalRepetitions}相反, customDisplayNameWithLongPattern()使用上述预定义 RepeatedTest.LONG_DISPLAY_NAME模式。

repeatedTestInGerman()演示了反复试验的显示名称翻译成外语的能力-在这种情况下德国,导致个人的重复,如名称:Wiederholung 1 von 5Wiederholung 2 von 5,等。

由于该beforeEach()方法被注释,@BeforeEach它将在每次重复测试的每次重复之前被执行。通过具有TestInfo与 RepetitionInfo注入的方法,我们可以看到,这是可能的,以获取有关当前正在执行的重复测试信息。执行启用RepeatedTestsDemo 的INFO日志级别将生成以下输出。

INFO:要执行重复1 of 10 for repeatedTest 
INFO:关于执行repeat 2 of 10 for repeatedTest 
INFO:关于执行repeat 3 of 10 for repeatedTest 
INFO:关于执行repeat 4 of 10 for repeatedTest 
INFO:关于执行重复5的10重复测试
信息:关于执行重复6的10重复测试
信息:关于执行重复7 of 10 for repeatedTest 
INFO:关于执行重复8 of 10 for repeatedTest 
INFO:关于执行重复9 of 10 for repeatedTest 
INFO :关于执行重复10 of 10 for repeatedTest 
INFO:关于执行repeat 1 of 5 for repeatedTestWithRepetitionInfo 
INFO:关于执行重复2 of 5 for repeatedTestWithRepetitionInfo 
INFO:关于执行repeat 3 of 5 for repeatedTestWithRepetitionInfo 
INFO:关于执行repeat 4 of 5 for repeatedTestWithRepetitionInfo 
INFO:关于执行repeat 5 of 5 for repeatedTestWithRepetitionInfo 
INFO:关于执行重复1 1 for customDisplayName 
INFO:关于执行重复1 of 1 for customDisplayNameWithLongPattern 
INFO:关于执行repeat 1 of 5 for repeatedTestInGerman 
INFO:关于执行repeat 2 of 5 for repeatedTestInGerman 
INFO:关于执行repeat 3 of 5 for repeatedTestInGerman 
INFO:About执行重复4 of 5 for repeatedTestInGerman 
INFO:关于执行重复5 of 5 for repeatedTestInGerman
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.logging.Logger;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import org.junit.jupiter.api.TestInfo;

class RepeatedTestsDemo {

    private Logger logger = // ...

    @BeforeEach
    void beforeEach(TestInfo testInfo, RepetitionInfo repetitionInfo) {
        int currentRepetition = repetitionInfo.getCurrentRepetition();
        int totalRepetitions = repetitionInfo.getTotalRepetitions();
        String methodName = testInfo.getTestMethod().get().getName();
        logger.info(String.format("About to execute repetition %d of %d for %s", //
            currentRepetition, totalRepetitions, methodName));
    }

    @RepeatedTest(10)
    void repeatedTest() {
        // ...
    }

    @RepeatedTest(5)
    void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) {
        assertEquals(5, repetitionInfo.getTotalRepetitions());
    }

    @RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}")
    @DisplayName("Repeat!")
    void customDisplayName(TestInfo testInfo) {
        assertEquals(testInfo.getDisplayName(), "Repeat! 1/1");
    }

    @RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME)
    @DisplayName("Details...")
    void customDisplayNameWithLongPattern(TestInfo testInfo) {
        assertEquals(testInfo.getDisplayName(), "Details... :: repetition 1 of 1");
    }

    @RepeatedTest(value = 5, name = "Wiederholung {currentRepetition} von {totalRepetitions}")
    void repeatedTestInGerman() {
        // ...
    }

}

当使用ConsoleLauncherjunitPlatformTest启用了unicode主题Gradle插件时RepeatedTestsDemo,将以下输出中结果执行到控制台。

├─RepeatedTestsDemo✔ 
│├─repeatedTest()✔ 
10││├─重复1✔ 
││├─重复2的10✔ 
││├─重复10 3✔ 
││├─10重复4✔ 
││├ - 重复5的
10✔││├─重复6的
10✔││├─重复7的
10✔││├─重复8的
10✔││├─重复9的
10✔││└─重复10的10 ✔ 
│├─repeatedTestWithRepetitionInfo(RepetitionInfo)✔ 
││├─5重复1✔ 
││├─重复2 5✔ 
││├─重复3 5✔的
││├─重复5 4✔ 
││└─重复5的
5✔│├─重复!
展示│展示│└─重复!1/ 
1✔│├─详细...

3.13。参数化测试

参数化测试使得可以用不同的参数多次运行测试。它们被声明为常规@Test方法,而是使用@ParameterizedTest注释。此外,您必须声明至少一个将为每个调用提供参数的

@ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
void testWithStringParameter(String argument) {
    assertNotNull(argument);
}

此参数化测试使用@ValueSource注释来指定String数组作为参数的来源。执行此方法时,将分别报告每次调用。例如,ConsoleLauncher将打印输出类似于以下内容。

testWithStringParameter(String)
✔├─[1]你好
✔└─[2]世界✔

3.13.1。必需设置

为了使用参数化测试,您需要添加对junit-jupiter-params 工件的依赖有关详细信息,请参阅依赖元数据。

3.13.2。论据的来源

开箱即用,JUnit Jupiter提供了很多注释。以下各小节提供了一个简要的概述和每个示例。org.junit.jupiter.params.provider有关其他信息,请参阅包中的JavaDoc 

@ValueSource

@ValueSource是最简单的来源之一。它,可以指定基本类型的文字(或者阵列Stringintlong,或double),并且可以仅被用于提供每次调用的单个参数。

@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
    assertNotNull(argument);
}
@EnumSource

@EnumSource提供了一种方便使用Enum常数的方法。注释提供了一个可选 names参数,允许您指定应使用哪些常量。如果省略,将使用所有常量,如下例所示。

@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithEnumSource(TimeUnit timeUnit) {
    assertNotNull(timeUnit);
}
@ParameterizedTest
@EnumSource(value = TimeUnit.class, names = { "DAYS", "HOURS" })
void testWithEnumSourceInclude(TimeUnit timeUnit) {
    assertTrue(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
}

The @EnumSource annotation also provides an optional mode parameter that enables fine-grained control over which constants are passed to the test method. For example, you can exclude names from the enum constant pool or specify regular expressions as in the following examples.

@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = EXCLUDE, names = { "DAYS", "HOURS" })
void testWithEnumSourceExclude(TimeUnit timeUnit) {
    assertFalse(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
    assertTrue(timeUnit.name().length() > 5);
}
@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = MATCH_ALL, names = "^(M|N).+SECONDS$")
void testWithEnumSourceRegex(TimeUnit timeUnit) {
    String name = timeUnit.name();
    assertTrue(name.startsWith("M") || name.startsWith("N"));
    assertTrue(name.endsWith("SECONDS"));
}
@MethodSource

@MethodSource允许您参考测试类的一种或多种方法。每个方法都必须返回一个Stream,一个Iterable,一个Iterator或一个数组的参数。另外,每个方法必须是static且不能接受任何参数。

如果您只需要一个参数,则可以直接返回参数类型的实例,如以下示例所示。

@ParameterizedTest
@MethodSource("stringProvider")
void testWithSimpleMethodSource(String argument) {
    assertNotNull(argument);
}

static Stream<String> stringProvider() {
    return Stream.of("foo", "bar");
}

还支持原始类型(DoubleStream,,IntStreamLongStream)的

@ParameterizedTest
@MethodSource("range")
void testWithRangeMethodSource(int argument) {
    assertNotEquals(9, argument);
}

static IntStream range() {
    return IntStream.range(0, 20).skip(10);
}

如果需要多个参数,则需要返回一个Arguments实例,如下所示。注意,这Arguments.of(Object…​)是一个在接口本身定义的静态工厂方法。

@ParameterizedTest
@MethodSource("stringAndIntProvider")
void testWithMultiArgMethodSource(String first, int second) {
    assertNotNull(first);
    assertNotEquals(0, second);
}

static Stream<Arguments> stringAndIntProvider() {
    return Stream.of(Arguments.of("foo", 1), Arguments.of("bar", 2));
}
@CsvSource

@CsvSource允许您将参数列表表示为逗号分隔值(即 String文字)。

@ParameterizedTest
@CsvSource({ "foo, 1", "bar, 2", "'baz, qux', 3" })
void testWithCsvSource(String first, int second) {
    assertNotNull(first);
    assertNotEquals(0, second);
}
@CsvFileSource

@CsvFileSource允许您使用类路径中的CSV文件。来自CSV文件的每一行都会导致一次调用参数化测试。

@ParameterizedTest
@CsvFileSource(resources = "/two-column.csv")
void testWithCsvFileSource(String first, int second) {
    assertNotNull(first);
    assertNotEquals(0, second);
}
两column.csv
foo, 1
bar, 2
"baz, qux", 3
@ArgumentsSource

@ArgumentsSource可以用来指定自定义,可重用ArgumentsProvider

@ParameterizedTest
@ArgumentsSource(MyArgumentsProvider.class)
void testWithArgumentsSource(String argument) {
    assertNotNull(argument);
}

static class MyArgumentsProvider implements ArgumentsProvider {

    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
        return Stream.of("foo", "bar").map(Arguments::of);
    }
}

3.13.3。参数转换

隐性转换

为了支持用例@CsvSource,JUnit Jupiter提供了一些内置的隐式类型转换器。转换过程取决于每个方法参数的声明类型。

例如,如果@ParameterizedTest声明一个类型的参数,TimeUnit并且由声明的源提供的实际类型为a String,则字符串将自动转换为相应的TimeUnit枚举常量。

@ParameterizedTest
@ValueSource(strings = "SECONDS")
void testWithImplicitArgumentConversion(TimeUnit argument) {
    assertNotNull(argument.name());
}

String 实例当前被隐式转换为以下目标类型。

目标类型

boolean/Boolean

"true" → true

byte/Byte

"1" → (byte) 1

char/Character

"o" → 'o'

short/Short

"1" → (short) 1

int/Integer

"1" → 1

long/Long

"1" → 1L

float/Float

"1.0" → 1.0f

double/Double

"1.0" → 1.0d

Enum 子类

"SECONDS" → TimeUnit.SECONDS

java.time.Instant

"1970-01-01T00:00:00Z" → Instant.ofEpochMilli(0)

java.time.LocalDate

"2017-03-14" → LocalDate.of(2017, 3, 14)

java.time.LocalDateTime

"2017-03-14T12:34:56.789" → LocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000)

java.time.LocalTime

"12:34:56.789" → LocalTime.of(12, 34, 56, 789_000_000)

java.time.OffsetDateTime

"2017-03-14T12:34:56.789Z" → OffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)

java.time.OffsetTime

"12:34:56.789Z" → OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC)

java.time.Year

"2017" → Year.of(2017)

java.time.YearMonth

"2017-03" → YearMonth.of(2017, 3)

java.time.ZonedDateTime

"2017-03-14T12:34:56.789Z" → ZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)

显式转换

而不是使用隐式参数转换,您可以ArgumentConverter使用@ConvertWith注释显式指定一个用于特定参数,如下例所示。

@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithExplicitArgumentConversion(@ConvertWith(ToStringArgumentConverter.class) String argument) {
    assertNotNull(TimeUnit.valueOf(argument));
}

static class ToStringArgumentConverter extends SimpleArgumentConverter {

    @Override
    protected Object convert(Object source, Class<?> targetType) {
        assertEquals(String.class, targetType, "Can only convert to String");
        return String.valueOf(source);
    }
}

明确的参数转换器是由测试作者来实现的。因此, junit-jupiter-params只提供一个可以作为参考实现的单个显式参数转换器:JavaTimeArgumentConverter它通过组合注释使用JavaTimeConversionPattern

@ParameterizedTest
@ValueSource(strings = { "01.01.2017", "31.12.2017" })
void testWithExplicitJavaTimeConverter(@JavaTimeConversionPattern("dd.MM.yyyy") LocalDate argument) {
    assertEquals(2017, argument.getYear());
}

3.13.4。自定义显示名称

默认情况下,参数化测试调用的显示名称包含调用索引和 String representation of all arguments for that specific invocation. However, you can customize invocation display names via the nameattribute of the @ParameterizedTest annotation like in the following example.

@DisplayName("Display name of container")
@ParameterizedTest(name = "{index} ==> first=''{0}'', second={1}")
@CsvSource({ "foo, 1", "bar, 2", "'baz, qux', 3" })
void testWithCustomDisplayNames(String first, int second) {
}

当执行上述方法时,ConsoleLauncher您将看到类似于以下内容的输出。

显示容器的名称
✔├─1 ==> first ='foo',second = 
1✔├─2 ==> first ='bar',second = 
2✔└─3 ==> first ='baz,qux' ,second = 3✔

在自定义显示名称中支持以下占位符。

占位符描述

{index}

当前的调用索引(1)

{arguments}

完整的逗号分隔的参数列表

{0}{1},...

个人论证

3.13.5。生命周期和互操作性

参数化测试的每次调用与常规@Test 方法具有相同的生命周期例如,@BeforeEach方法将在每次调用之前执行。动态测试类似,调用将在IDE的测试树中逐个显示。您可以在 同一测试类中混合常规的@Test方法和@ParameterizedTest方法。

您可以使用ParameterResolver扩展名与@ParameterizedTest方法。但是,由参数源解析的方法参数需要先在参数列表中。由于测试类可能包含常规测试以及具有不同参数列表的参数化测试,因此参数源的值不会针对生命周期方法(例如@BeforeEach)和测试类构造函数进行解析 

@BeforeEach
void beforeEach(TestInfo testInfo) {
    // ...
}

@ParameterizedTest
@ValueSource(strings = "foo")
void testWithRegularParameterResolver(String argument, TestReporter testReporter) {
    testReporter.publishEntry("argument", argument);
}

@AfterEach
void afterEach(TestInfo testInfo) {
    // ...
}

3.14。测试模板

一种@TestTemplate方法不是一个常规的测试用例,而是一个用于测试用例的模板。因此,它被设计为多次调用,这取决于注册提供商返回的调用上下文的数量。因此,它必须与注册的TestTemplateInvocationContextProvider扩展名一起使用每个调用测试模板方法的行为就像一个常规@Test方法的执行,并且完全支持相同的生命周期回调和扩展。请参阅 为测试模板提供调用上下文以供使用示例。

3.15。动态测试

@Test注释中描述的JUnit Jupiter中的标准注释与JUnit 4中的注释 非常相似。@Test两者都描述了实现测试用例的方法。这些测试用例在编译时完全指定的意义上是静态的,并且由于运行时发生的任何事情都不能改变它们的行为。 假设提供了一种动态行为的基本形式,但是它们的表现力有意义上相当有限。

除了这些标准测试之外,JUnit Jupiter还引入了一种全新的测试编程模型。这种新的测试是一个动态测试,它是在运行时通过注释的工厂方法生成的@TestFactory

@Test方法相比,方法@TestFactory本身不是测试用例,而是测试用例的工厂。因此,动态测试是工厂的产物。从技术上讲,一个@TestFactory方法必须返回一个Stream, CollectionIterable,或IteratorDynamicNode实例。的实例化的子类DynamicNodeDynamicContainerDynamicTest。 DynamicContainer实例由显示名称和动态子节点列表组成,使得能够创建动态节点的任意嵌套层次结构。 DynamicTest然后,实例将被懒惰地执行,从而实现动态甚至非确定性的测试用例生成。

任何Stream由a返回的任何内容@TestFactory都将通过调用正确关闭stream.close(),使其可以安全地使用资源Files.lines()

@Test方法一样,@TestFactory方法不能是privatestatic 可以选择声明要解析的参数ParameterResolvers

DynamicTest是在运行时生成的测试用例。它由一个显示名称和一个ExecutableExecutable@FunctionalInterface ,这意味着动态测试的实施方式可以被提供为lambda表达式 或方法的引用

 
动态测试生命周期
动态测试的执行生命周期与标准@Test情况完全不同具体来说,对于单个动态测试,没有生命周期回调。这意味着,@BeforeEach@AfterEach方法以及它们的相应的延伸回调被用于执行@TestFactory方法,但没有为每一个动态试验换句话说,如果从动态测试的lambda表达式中的测试实例访问字段,这些字段将不会被执行由相同@TestFactory方法生成的各个动态测试之间的回调方法或扩展重置

从JUnit Jupiter 5.0.0-M5开始,动态测试必须始终由工厂方法创建; 然而,这可能在以后的版本中由注册设施补充。

3.15.1。动态测试示例

以下DynamicTestsDemo类演示了测试工厂和动态测试的几个示例。

第一种方法返回无效的返回类型。由于在编译时无法检测到无效的返回类型,JUnitException因此在运行时检测到异常返回类型

接下来的五个方法是演示的产生很简单的例子 CollectionIterableIterator,或StreamDynamicTest实例。这些例子中的大多数并不真正表现出动态行为,而只是原则上证明了支持的返回类型。然而,dynamicTestsFromStream()dynamicTestsFromIntStream()演示了为给定的一组字符串或一系列输入数字生成动态测试是多么容易。

下一个方法本质上是真正的动态的。 generateRandomNumberOfTests()实现一个Iterator生成随机数,显示名称生成器和测试执行器,然后提供所有三个DynamicTest.stream()虽然非确定性行为generateRandomNumberOfTests()当然与测试重复性相冲突,因此应该小心使用,但它有助于证明动态测试的表现力和力量。

最后一个方法生成使用的动态测试的嵌套层次结构DynamicContainer

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.function.ThrowingConsumer;

class DynamicTestsDemo {

    // This will result in a JUnitException!
    @TestFactory
    List<String> dynamicTestsWithInvalidReturnType() {
        return Arrays.asList("Hello");
    }

    @TestFactory
    Collection<DynamicTest> dynamicTestsFromCollection() {
        return Arrays.asList(
            dynamicTest("1st dynamic test", () -> assertTrue(true)),
            dynamicTest("2nd dynamic test", () -> assertEquals(4, 2 * 2))
        );
    }

    @TestFactory
    Iterable<DynamicTest> dynamicTestsFromIterable() {
        return Arrays.asList(
            dynamicTest("3rd dynamic test", () -> assertTrue(true)),
            dynamicTest("4th dynamic test", () -> assertEquals(4, 2 * 2))
        );
    }

    @TestFactory
    Iterator<DynamicTest> dynamicTestsFromIterator() {
        return Arrays.asList(
            dynamicTest("5th dynamic test", () -> assertTrue(true)),
            dynamicTest("6th dynamic test", () -> assertEquals(4, 2 * 2))
        ).iterator();
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromStream() {
        return Stream.of("A", "B", "C")
            .map(str -> dynamicTest("test" + str, () -> { /* ... */ }));
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromIntStream() {
        // Generates tests for the first 10 even integers.
        return IntStream.iterate(0, n -> n + 2).limit(10)
            .mapToObj(n -> dynamicTest("test" + n, () -> assertTrue(n % 2 == 0)));
    }

    @TestFactory
    Stream<DynamicTest> generateRandomNumberOfTests() {

        // Generates random positive integers between 0 and 100 until
        // a number evenly divisible by 7 is encountered.
        Iterator<Integer> inputGenerator = new Iterator<Integer>() {

            Random random = new Random();
            int current;

            @Override
            public boolean hasNext() {
                current = random.nextInt(100);
                return current % 7 != 0;
            }

            @Override
            public Integer next() {
                return current;
            }
        };

        // Generates display names like: input:5, input:37, input:85, etc.
        Function<Integer, String> displayNameGenerator = (input) -> "input:" + input;

        // Executes tests based on the current input value.
        ThrowingConsumer<Integer> testExecutor = (input) -> assertTrue(input % 7 != 0);

        // Returns a stream of dynamic tests.
        return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor);
    }

    @TestFactory
    Stream<DynamicNode> dynamicTestsWithContainers() {
        return Stream.of("A", "B", "C")
            .map(input -> dynamicContainer("Container " + input, Stream.of(
                dynamicTest("not null", () -> assertNotNull(input)),
                dynamicContainer("properties", Stream.of(
                    dynamicTest("length > 0", () -> assertTrue(input.length() > 0)),
                    dynamicTest("not empty", () -> assertFalse(input.isEmpty()))
                ))
            )));
    }

}

4.运行测试

4.1。IDE支持

4.1.1。IntelliJ IDEA

IntelliJ IDEA支持自版本2016.2以来在JUnit平台上运行测试。有关详细信息,请参阅IntelliJ IDEA博客上的帖子

表1. JUnit 5版本捆绑在IntelliJ IDEA中
IntelliJ IDEA版本捆绑的JUnit 5版本

2016.2

M2

2016年3月1日

M3

2017年1月2日

M4

  IntelliJ IDEA捆绑某个版本的JUnit 5.这意味着,如果要使用更新的里程碑版本的Jupiter API,执行测试可能无法正常工作。一旦发布了第一个GA版本的JUnit 5,这种情况就会改善。在此期间,请按照以下说明使用比IntelliJ IDEA更新的JUnit 5版本。

为了使用不同的JUnit 5.0版本,你必须手动添加 junit-platform-launcherjunit-jupiter-enginejunit-vintage-engineJAR文件到classpath中。

附加的梯度依赖性
// Only needed to run tests in an IntelliJ IDEA that bundles an older version
testRuntime("org.junit.platform:junit-platform-launcher:1.0.0-M5")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0-M5")
testRuntime("org.junit.vintage:junit-vintage-engine:4.12.0-M5")
其他Maven依赖关系
<!-- Only required to run tests in an IntelliJ IDEA that bundles an older version -->
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.0.0-M5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.0.0-M5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>4.12.0-M5</version>
    <scope>test</scope>
</dependency>

4.1.2。Eclipse Beta支持

Eclipse 4.7(Oxygen)对JUnit Platform和JUnit Jupiter有beta支持。有关如何设置的详细信息,请参阅Eclipse JDT UI / JUnit 5 wiki页面。

4.1.3。其他IDE

在撰写本文时,没有直接支持在IDE中运行JUnit Platform的测试,而不是使用IntelliJ IDEA或Eclipse中的beta支持。然而,JUnit团队提供了两个中间解决方案,以便您可以继续在今天的IDE中尝试JUnit 5。您可以 手动使用控制台启动器,也可以使用基于JUnit 4的Runner执行测试

4.2。构建支持

4.2.1。摇篮

JUnit团队开发了一个非常基本的Gradle插件,允许您运行任何类型的测试TestEngine(例如JUnit 3,JUnit 4,JUnit Jupiter, Specsy等)。build.gradlejunit5-gradle-consumer项目中看到一个插件的例子。

启用JUnit Gradle插件

要使用JUnit Gradle插件,您首先需要确保您正在运行Gradle 2.5或更高版本。一旦你这样做,你可以配置build.gradle如下。

buildscript {
    repositories {
        mavenCentral()
        // The following is only necessary if you want to use SNAPSHOT releases.
        // maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
    }
    dependencies {
        classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-M5'
    }
}

apply plugin: 'org.junit.platform.gradle.plugin'
配置JUnit Gradle插件

一旦应用JUnit Gradle插件,您可以如下进行配置。

  这些选项很可能会随着我们继续努力向最终版本而发生变化。
junitPlatform {
    platformVersion 1.0
    logManager 'org.apache.logging.log4j.jul.LogManager'
    reportsDir file('build/test-results/junit-platform') // this is the default
    // enableStandardTestTask true
    // selectors (optional)
    // filters (optional)
}

设置logManager指示JUnit Gradle插件将java.util.logging.manager系统属性设置为 要使用实现的提供的完全限定的类java.util.logging.LogManager上面的例子演示了如何配置log4j LogManager

默认情况下,JUnit Gradle插件会禁用标准的Gradle test任务,但可以通过该enableStandardTestTask标志来覆盖

配置选择器

默认情况下,插件将扫描项目的输出目录以进行测试。但是,您可以使用selectors扩展元素指定要显式执行哪些测试

junitPlatform {
    // ...
    selectors {
        uris 'file:///foo.txt', 'http://example.com/'
        uri 'foo:resource' 
        files 'foo.txt', 'bar.csv'
        file 'qux.json' 
        directories 'foo/bar', 'bar/qux'
        directory 'qux/bar' 
        packages 'com.acme.foo', 'com.acme.bar'
        aPackage 'com.example.app' 
        classes 'com.acme.Foo', 'com.acme.Bar'
        aClass 'com.example.app.Application' 
        methods 'com.acme.Foo#a', 'com.acme.Foo#b'
        method 'com.example.app.Application#run(java.lang.String[])' 
        resources '/bar.csv', '/foo/input.json'
        resource '/com/acme/my.properties' 
    }
    // ...
}
  的URI
  本地文件
  Local directories
 
  类,完全限定类名
  方法,完全限定方法名称(请参阅DiscoverySelectors中的selectMethod(String)
  类路径资源
配置过滤器

您可以使用filters扩展名为测试计划配置过滤器默认情况下,所有引擎和标签都包含在测试计划中。只有默认 includeClassNamePattern^.*Tests?$)被应用。您可以覆盖默认模式,如以下示例所示。当您指定多个模式时,它们将使用OR语义进行组合。

junitPlatform {
    // ...
    filters {
        engines {
            include 'junit-jupiter'
            // exclude 'junit-vintage'
        }
        tags {
            include 'fast', 'smoke'
            // exclude 'slow', 'ci'
        }
        packages {
            include 'com.sample.included1', 'com.sample.included2'
            // exclude 'com.sample.excluded1', 'com.sample.excluded2'
        }
        includeClassNamePattern '.*Spec'
        includeClassNamePatterns '.*Test', '.*Tests'
    }
    // ...
}

如果您通过提供测试引擎ID,则JUnit Gradle插件将仅运行测试所需的测试引擎。同样,如果您通过通过JUnit Gradle插件提供标签,则只会运行相应标记的测试(例如,通过JUnit Jupiter测试注释)。同样适用于可以使用包含或排除的包名称engines {include …​}engines {exclude …​}tags {include …​}tags {exclude …​}@Tagpackages {include …​}packages {exclude …​}

配置测试引擎

为了使JUnit Gradle插件完全运行任何测试,一个TestEngine 实现必须在类路径上。

要配置对基于JUnit Jupiter的测试的支持,请配置testCompileJUnit Jupiter API的testRuntime依赖关系以及与JUnit Jupiter TestEngine实现类似依赖关系

dependencies {
    testCompile("org.junit.jupiter:junit-jupiter-api:5.0.0-M5")
    testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0-M5")
}

JUnit Gradle插件可以运行基于JUnit 4的测试,只要您配置 testCompile对JUnit 4的testRuntime依赖关系,以及与JUnit Vintage TestEngine实现类似依赖关系 即可。

dependencies {
    testCompile("junit:junit:4.12")
    testRuntime("org.junit.vintage:junit-vintage-engine:4.12.0-M5")
}
使用JUnit Gradle插件

一旦JUnit Gradle插件被应用和配置,你有一个新的 junitPlatformTest任务可供您使用。

从命令行调用gradlew junitPlatformTest(或gradlew test)将在项目中执行所有测试,其类名称与通过includeClassNamePattern(默认为 ^.*Tests?$提供的正则表达式匹配

执行项目中junitPlatformTest任务会junit5-gradle-consumer产生类似于以下内容的输出:

:junitPlatformTest 

测试运行完成93 ms 
[发现3个容器] 
[0容器已跳过] 
[3容器已启动
] 
[3容器中止] [3容器成功] 
[0容器失败] 
[3测试发现] 
[1测试跳过] 
[2测试开始] 
[0测试中止] 
[2测试成功] 
[0测试失败] 

BUILD SUCCESSFUL

如果测试失败,则生成将失败,输出类似于以下内容:


经过99毫秒试运行结束
[发现3个箱] 
[0集装箱跳过] 
[3个容器开始] 
[0容器中止] 
[3个容器成功] 
[0容器未能] 
[发现3个测试] 
[0测试跳过] 
[3个测试开始] 
[0 tests aborted] 
[2 tests successful] 
[1 tests failed] 
:junitPlatformTest FAILED 
FAILURE:Build failed with a exception。
*出了什么问题:
执行失败的任务':junitPlatformTest'。





> Process 'command '/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1
  出口值1,如果任何容器或测试失败; 否则的话0
 
JUnit Gradle插件的当前限制
通过JUnit Gradle插件运行的任何测试的结果将不会包含在Gradle生成的标准测试报告中; 但是,测试结果通常可以在CI服务器上进行聚合。看到reportsDir插件属性。

4.2.2。Maven的

JUnit团队为Maven Surefire开发了一个非常基本的提供程序,可让您运行JUnit 4和JUnit Jupiter测试mvn test项目中pom.xml文件 junit5-maven-consumer演示了如何使用它,并可作为起点。

  由于Surefire 2.20内存泄漏,junit-platform-surefire-provider 目前仅适用于Surefire 2.19.1。
...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
                    <version>1.0.0-M5</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>
...
配置测试引擎

为了让Maven Surefire完全运行任何测试,TestEngine必须将一个实现添加到运行时类路径中。

要配置对基于JUnit Jupiter的测试的支持,请test对JUnit Jupiter API 配置依赖关系,并将JUnit Jupiter TestEngine实现添加maven-surefire-plugin类似于以下内容的依赖项 

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
                    <version>1.0.0-M5</version>
                </dependency>
                <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-engine</artifactId>
                    <version>5.0.0-M5</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>
...
<dependencies>
    ...
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.0.0-M5</version>
        <scope>test</scope>
    </dependency>
</dependencies>
...

JUnit Platform Surefire Provider可以运行基于JUnit 4的测试,只要您配置test对JUnit 4 的 依赖,并将JUnit Vintage TestEngine实现添加maven-surefire-plugin到与以下类似的依赖项

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
                    <version>1.0.0-M5</version>
                </dependency>
                ...
                <dependency>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                    <version>4.12.0-M5</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>
...
<dependencies>
    ...
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
...
按标签过滤

您可以使用以下配置属性过滤测试。

  • 包括标签,使用groupsincludeTags

  • 要排除标签,请使用excludedGroupsexcludeTags

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <configuration>
                <properties>
                    <includeTags>acceptance</includeTags>
                    <excludeTags>integration, regression</excludeTags>
                </properties>
            </configuration>
            <dependencies>
                ...
            </dependencies>
        </plugin>
    </plugins>
</build>
...

4.3。控制台启动器

ConsoleLauncher是一个命令行Java应用程序,可让您从控制台启动JUnit平台。例如,它可以用于运行JUnit Vintage和JUnit Jupiter测试,并将测试执行结果打印到控制台。

junit-platform-console-standalone-1.0.0-M5.jar包含所有依赖性的可执行文件junit-platform-console-standalone 目录下的Maven中央存储库中发布 您可以独立运行ConsoleLauncher,如下所示。

java -jar junit-platform-console-standalone-1.0.0-M5.jar <Options>

以下是其输出的示例:

├─JUnit复古
│└─example.JUnit4Tests
│└─standardJUnit4Test✔
└─JUnit木星
   ├─标准测试
   │├─succeedingTest()✔
   │└─skippedTest()↷用于演示
   └─一个特殊的测试用例
      ├─包含空格的自定义测试名✔
      ├─╯°□°)╯✔
      └─😱✔

测试运行在64 ms后完成
[发现5个集装箱]
[0容器跳过]
[5容器开始]
[0容器中止]
[5容器成功]
[0容器失败]
[发现6项测试]
[跳过1次测试]
[5测试开始]
[0测试中止]
[5次测试成功]
[0测试失败]
 
退出代码
ConsoleLauncher具有状态代码 的出口(1如果有)容器或测试失败。否则退出代码是0

4.3.1。选项

  这些选项很可能会随着我们继续努力向最终版本而发生变化。
选项说明
------ -----------
-h,--help显示帮助信息。
--disable-ansi-colors在输出中禁用ANSI颜色(不是
                                                由所有终端支持)。
- 详细信息<[无,平,树,详细]>选择时间的输出详细信息模式
                                                测试执行。使用以下之一:[无,
                                                平,树,详细]。如果没有
                                                选择,然后只进行总结和测试
                                                显示失败。(默认:树)
--details-theme <[ascii,unicode]>选择一个输出详细信息树主题
                                                当执行测试时。使用以下之一:
                                                [ascii,unicode](默认:unicode)
--class-path,--classpath,--cp <Path:提供其他类路径条目 - 
  path1:path2:...>例如添加引擎和
                                                他们的依赖。这个选项可以
                                                重复。
--reports-dir <Path>将报告输出设置为指定的
                                                本地目录(如果它将被创建)
                                                不存在)。
--scan-class-path,--scan-classpath [路径:扫描类路径上的所有目录或
  path1:path2:...]显式类路径根。没有
                                                参数,只有目录
                                                系统类路径以及附加功能
                                                通过-cp提供的类路径条目
                                                (目录和JAR文件)被扫描。
                                                不显示的类路径根
                                                类路径将被默认忽略。
                                                可以重复此选项。
-u,--select-uri <URI>选择测试发现的URI。这个
                                                选项可以重复。
-f,--select-file <String>选择要测试发现的文件。这个
                                                选项可以重复。
-d,--select-directory <String>选择用于测试发现的目录。
                                                可以重复此选项。
-p,--select-package <String>选择用于测试发现的包。这个
                                                选项可以重复。
-c,--select-class <String>为测试发现选择一个类。这个
                                                选项可以重复。
-m,--select-method <String>选择测试发现的方法。这个
                                                选项可以重复。
-r,--select-resource <String>选择一个用于测试的类路径资源
                                                发现。可以重复此选项。
-n,--include-classname <String>提供要包含的正则表达式
                                                只有具有完全限定名称的班级
                                                比赛。避免加载类
                                                不必要地,只有默认模式
                                                包括结束的类名
                                                “测试”或“测试”。当这个选项是
                                                重复,所有的模式将被组合
                                                使用OR语义。(默认值:^。*测试?$)
-N,--exclude-classname <String>提供要排除的正则表达式
                                                那些完全合格的班级
                                                名字匹配。当这个选项是
                                                重复,所有的模式将被组合
                                                使用OR语义。
--include-package <String>提供要包含的包
                                                测试运行。可以重复此选项。
--exclude-package <String>提供要从中排除的包
                                                测试运行。可以重复此选项。
-t,--include-tag <String>提供要包含在测试中的标签
                                                跑。可以重复此选项。
-T,--exclude-tag <String>提供要从测试中排除的标签
                                                跑。可以重复此选项。
-e,--include-engine <String>提供要包含的引擎的ID
                                                在测试运行。这个选项可以
                                                重复。
-E,--exclude-engine <String>提供要排除的引擎的ID
                                                从测试运行。这个选项可以
                                                重复。

4.4。使用JUnit 4运行JUnit平台

运行程序是JUnitPlatform基于JUnit 4的Runner,它使您可以在JUnit 4环境中运行JUnit Platform上支持其编程模型的任何测试 - 例如JUnit Jupiter测试类。

注释类,@RunWith(JUnitPlatform.class)允许它使用IDE运行,并构建支持JUnit 4但尚未直接支持JUnit平台的系统。

  由于JUnit平台具有JUnit 4没有的功能,所以运行程序只能支持JUnit Platform功能的一个子集,特别是在报告方面(请参阅显示名称与技术名称)。但目前来说,JUnitPlatform跑步者是一种简单的入门方式。

4.4.1。建立

您需要以下工件及其对类路径的依赖关系。有关组ID,工件ID和版本的详细信息,请参阅 依赖元数据

明确依赖
  • junit-4.12.jar测试范围内:使用JUnit 4运行测试。

  • junit-platform-runner测试范围内:JUnitPlatform跑步者的位置

  • junit-jupiter-api测试范围:API编写测试,包括@Test

  • junit-jupiter-engine测试运行时间范围内:实现JUnit Jupiter的Engine API

传递依赖关系
  • junit-platform-launcher测试范围内

  • junit-platform-engine测试范围内

  • junit-platform-commons测试范围内

  • opentest4j测试范围内

4.4.2。显示名称与技术名称

默认情况下,显示名称将用于测试工件; 然而,当运行 JUnitPlatform程序用于使用诸如Gradle或Maven之类的构建工具执行测试时,生成的测试报告通常需要包括测试工件技术名称,例如完全限定类名称,而不是较短的显示名称,如测试类的简单名称或包含特殊字符的自定义显示名称。为了使技术名称能够进行报告,只需简单地声明 @UseTechnicalNames注释@RunWith(JUnitPlatform.class)

4.4.3。单考试班

使用运行程序的一种方法JUnitPlatform是直接注释测试类 @RunWith(JUnitPlatform.class)请注意,以下示例中的测试方法用org.junit.jupiter.api.Test(JUnit Jupiter)注释,而不是org.junit.Test(JUnit Vintage)。此外,在这种情况下,测试类必须是public否则,IDE将不会将其识别为JUnit 4测试类。

import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;

@RunWith(JUnitPlatform.class)
public class JUnit4ClassDemo {

    @Test
    void succeedingTest() {
        /* no-op */
    }

    @Test
    void failingTest() {
        fail("Failing for failing's sake.");
    }

}

4.4.4。测试套件

如果您有多个测试类,则可以创建一个测试套件,如以下示例所示。

import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.runner.RunWith;

@RunWith(JUnitPlatform.class)
@SelectPackages("example")
public class JUnit4SuiteDemo {
}

JUnit4SuiteDemo会发现并运行所有测试example包及其子包。默认情况下,它只包含名称与模式匹配的测试类^.*Tests?$

 
其他配置选项
有更多的配置选项用于发现和过滤测试,而不仅仅是 @SelectPackages有关详细信息,请咨询 Javadoc

扩展模型

5.1。概观

与此相反的竞争Runner@Rule以及@ClassRule延伸在JUnit的4个点,所述的JUnit木星扩展模型由一个单一的,连贯的概念的:所述 ExtensionAPI。但是请注意,它Extension本身只是一个标记界面。

5.2。注册扩展

扩展可以通过或通过Java 机制自动 注册 @ExtendWithServiceLoader

5.2.1。声明扩展注册

开发人员可以通过注释测试接口,测试类,测试方法或定制组合注释声明性地注册一个或多个扩展并为注册扩展提供类引用。@ExtendWith(…​)

例如,要注册MockitoExtension特定测试方法的自定义,您将按照以下方式注释测试方法。

@ExtendWith(MockitoExtension.class)
@Test
void mockTest() {
    // ...
}

MockitoExtension在特定类及其子类中注册所有测试的自定义,您将按照以下方式对测试类进行注释。

@ExtendWith(MockitoExtension.class)
class MockTests {
    // ...
}

多个扩展可以一起注册:

@ExtendWith({ FooExtension.class, BarExtension.class })
class MyTestsV1 {
    // ...
}

作为替代方案,可以分开注册多个扩展名,如下所示:

@ExtendWith(FooExtension.class)
@ExtendWith(BarExtension.class)
class MyTestsV2 {
    // ...
}

测试中都执行MyTestsV1MyTestsV2将被延长 FooExtension,并BarExtension在准确的顺序。

5.2.2。自动分机注册

除了 使用注释的声明式扩展注册支持外,JUnit Jupiter还支持 通过Java 机制进行全局扩展注册java.util.ServiceLoader,允许根据类路径中可用的内容自动检测并自动注册第三方扩展。

具体来说,可以通过在其封闭的JAR文件中org.junit.jupiter.api.extension.Extension/META-INF/services文件夹中命名的 文件中提供其全限定类名来注册自定义扩展

启用自动扩展检测

自动检测是高级功能,因此默认情况下不启用。要启用它,只需将junit.extensions.autodetection.enabled配置键设置true这可以作为JVM系统属性或作为传递给该参数配置参数提供 LauncherDiscoveryRequestLauncher

例如,要启用扩展的自动检测,可以使用以下系统属性启动JVM。

-Djunit.extensions.autodetection.enabled=true

如果启用自动检测,通过发现扩展ServiceLoader机制将被添加到JUnit的木星的全球扩展后的扩展注册表(例如,用于支持TestInfoTestReporter等等)。

5.2.3。扩展继承

注册的扩展名在具有自顶向下语义的测试类层次结构中继承。类似地,在类级别注册的扩展在方法级继承。此外,特定的扩展实现只能针对给定的扩展上下文及其父上下文注册一次。因此,任何注册重复扩展实现的尝试都将被忽略。

5.3。条件测试执行

ExecutionCondition定义Extension用于程序化,条件测试执行API 

一个ExecutionCondition评估的每个容器(例如,一个测试类),以确定是否它包含的所有测试应基于所提供的执行 ExtensionContext类似地,ExecutionCondition评价为每个测试,以确定是否一个给定的测试方法应根据所提供的被执行 ExtensionContext

ExecutionCondition注册了多个扩展名时,一旦某个条件返回禁用,容器或测试就被禁用因此,不能保证评估条件,因为另一个扩展可能已经导致容器或测试被禁用。换句话说,评估工作类似于短路布尔OR运算符。

看到的源代码DisabledCondition@Disabled为具体例子。

5.3.1。停用条件

有时,运行测试套件没有一定条件是有效的。例如,您可能希望运行测试,即使它们已注释,@Disabled以查看它们是否仍然损坏要做到这一点,只需提供junit.conditions.deactivate配置密钥的模式, 指定当前测试运行中哪些条件应该被禁用(即不被评估)。该模式可以作为JVM系统属性提供,也可以作为 传递给该参数配置参数提供LauncherDiscoveryRequestLauncher

例如,要停用JUnit的@Disabled条件,可以使用以下系统属性启动JVM。

-Djunit.conditions.deactivate=org.junit.*DisabledCondition

模式匹配语法

如果junit.conditions.deactivate模式仅由星号(*)组成,则所有条件都将被禁用。否则,该模式将用于匹配每个注册条件的完全限定类名(FQCN)。模式中的任何点(.)将与FQCN 中的点(.)或美元符号($)匹配。任何星号(*)将与FQCN中的一个或多个字符匹配。该模式中的所有其他字符将与FQCN一对一匹配。

例子:

  • *:停用所有条件。

  • org.junit.*:禁用org.junit基本包装下的每个条件及其任何子包。

  • *.MyCondition:取消激活其简单类名称完全的每个条件 MyCondition

  • *System*:取消激活简单类名包含的每个条件System

  • org.example.MyCondition:取消激活FQCN正确的条件 org.example.MyCondition

5.4。测试实例后处理

TestInstancePostProcessor定义了Extensions希望发布过程测试实例的API 

通常的用例包括将依赖关系注入到测试实例中,在测试实例上调用自定义初始化方法等。

对于具体的例子,请查阅的源代码MockitoExtension和 SpringExtension

5.5。参数分辨率

ParameterResolver定义了Extension在运行时动态解析参数API。

如果测试构造函数或@Test@TestFactory@BeforeEach@AfterEach, @BeforeAll,或@AfterAll方法接受一个参数,该参数必须解决 在运行时由一个ParameterResolverParameterResolver可以内置(见 TestInfoParameterResolver)或用户注册一般来说,参数可以通过名称类型注释或其任何组合来解决具体的例子请参考 CustomTypeParameterResolverCustomAnnotationParameterResolver

5.6。测试生命周期回调

以下接口定义用于在测试执行生命周期中的各个点扩展测试的API。有关org.junit.jupiter.api.extension更多详细信息,请参阅以下部分,了解中的每个接口的示例和Javadoc 

 
实现多个扩展API
扩展开发人员可以选择在单个扩展中实现任意数量的这些接口。请参阅SpringExtension具体示例的源代码

5.6.1。测试执行回调之前和之后

BeforeTestExecutionCallbackAfterTestExecutionCallback定义了Extensions那些希望添加将在执行测试方法之前和 之后立即执行的行为的API 因此,这些回调非常适合于定时,跟踪和类似的用例。如果你需要实现时调用的回调周围 @BeforeEach@AfterEach方法,落实BeforeEachCallbackAfterEachCallback代替。

以下示例显示如何使用这些回调来计算和记录测试方法的执行时间。TimingExtension实现两者BeforeTestExecutionCallback 并AfterTestExecutionCallback为了时间和日志测试执行。

一个扩展,用于测试方法的执行时间和日志
import java.lang.reflect.Method;
import java.util.logging.Logger;

import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;

public class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {

    private static final Logger LOG = Logger.getLogger(TimingExtension.class.getName());

    @Override
    public void beforeTestExecution(ExtensionContext context) throws Exception {
        getStore(context).put(context.getTestMethod().get(), System.currentTimeMillis());
    }

    @Override
    public void afterTestExecution(ExtensionContext context) throws Exception {
        Method testMethod = context.getTestMethod().get();
        long start = getStore(context).remove(testMethod, long.class);
        long duration = System.currentTimeMillis() - start;

        LOG.info(() -> String.format("Method [%s] took %s ms.", testMethod.getName(), duration));
    }

    private Store getStore(ExtensionContext context) {
        return context.getStore(Namespace.create(getClass(), context));
    }

}

由于TimingExtensionTests课程注册TimingExtension通过@ExtendWith,它的测试将在执行时应用这个时序。

使用示例TimingExtension的测试类
@ExtendWith(TimingExtension.class)
class TimingExtensionTests {

    @Test
    void sleep20ms() throws Exception {
        Thread.sleep(20);
    }

    @Test
    void sleep50ms() throws Exception {
        Thread.sleep(50);
    }

}

以下TimingExtensionTests是运行时生成的日志记录的示例

信息:方法[sleep20ms]花了24 ms。
信息:方法[sleep50ms]花了53 ms。

5.7。异常处理

TestExecutionExceptionHandler定义了Extensions希望处理测试执行期间抛出的异常的API 

以下示例显示了一个扩展名,它将吞并所有实例,IOException 但重新抛出任何其他类型的异常。

异常处理扩展
public class IgnoreIOExceptionExtension implements TestExecutionExceptionHandler {

    @Override
    public void handleTestExecutionException(ExtensionContext context, Throwable throwable)
            throws Throwable {

        if (throwable instanceof IOException) {
            return;
        }
        throw throwable;
    }
}

5.8。提供测试模板的调用上下文

一个@TestTemplate方法只能在至少一个TestTemplateInvocationContextProvider注册时执行 每一个这样的提供者负责提供StreamTestTemplateInvocationContext实例。每个上下文都可以指定自定义显示名称和附加扩展名单,仅用于下一次调用该@TestTemplate方法。

以下示例说明如何编写测试模板以及如何注册和实现TestTemplateInvocationContextProvider

带有附带扩展名的测试模板
@TestTemplate
@ExtendWith(MyTestTemplateInvocationContextProvider.class)
void testTemplate(String parameter) {
    assertEquals(3, parameter.length());
}

static class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        return true;
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
        return Stream.of(invocationContext("foo"), invocationContext("bar"));
    }

    private TestTemplateInvocationContext invocationContext(String parameter) {
        return new TestTemplateInvocationContext() {
            @Override
            public String getDisplayName(int invocationIndex) {
                return parameter;
            }

            @Override
            public List<Extension> getAdditionalExtensions() {
                return Collections.singletonList(new ParameterResolver() {
                    @Override
                    public boolean supportsParameter(ParameterContext parameterContext,
                            ExtensionContext extensionContext) {
                        return parameterContext.getParameter().getType().equals(String.class);
                    }

                    @Override
                    public Object resolveParameter(ParameterContext parameterContext,
                            ExtensionContext extensionContext) {
                        return parameter;
                    }
                });
            }
        };
    }
}

在这个例子中,测试模板将被调用两次。调用的显示名称将是调用上下文指定的“foo”和“bar”。每个调用都会注册一个ParameterResolver用于解析方法参数的自定义使用时的输出ConsoleLauncher如下。

└─testTemplate(String)✔
   ├─foo✔
   └─酒吧✔

TestTemplateInvocationContextProvider扩展API的主要目的是用于实现不同种类的依赖于测试样方法的重复调用虽然在不同的背景下测试-例如,使用不同的参数,通过准备测试类的实例不同,或多次,而无需修改上下文。请参阅使用此扩展点重复测试或 参数化测试的实现来提供其功能。

5.9。保持扩张

通常,一个扩展只被实例化一次。所以这个问题变得相关:你如何将状态从一个调用扩展到下一个?ExtensionContextAPI提供了Store准确用于这一目的。扩展可以将值放入商店以供以后检索。请参阅TimingExtension使用Store方法级范围的示例 重要的是记住ExtensionContext在测试执行期间存储的值 在周围不可用 ExtensionContext由于ExtensionContexts可以嵌套,内部上下文的范围也可能受到限制。有关可用于存储和检索值的方法的详细信息,请参阅相应的Javadoc Store

5.10。扩展中支持的实用程序

JUnit Platform Commons工件公开了一个名为的包 org.junit.platform.commons.support,其中包含用于处理注释,反射和类路径扫描任务的维护实用程序方法。TestEngineExtension 鼓励作者使用这些支持的方法,以便与JUnit平台的行为保持一致。

5.11。用户代码和扩展的相对执行顺序

当执行包含一个或多个测试方法的测试类时,除了用户提供的测试和生命周期方法之外,还会调用多个扩展回调。下图说明了用户提供的代码和扩展代码的相对顺序。

扩展生命周期
用户代码和扩展代码

用户提供的测试和生命周期方法以橙色显示,回调代码由蓝色显示。灰色框表示单个测试方法的执行,并将在测试类中对每个测试方法重复执行。

下表进一步说明了用户代码和扩展代码图中的十二个步骤 

接口/注释描述

1

接口 org.junit.jupiter.api.extension.BeforeAllCallback

执行所有容器测试之前执行的扩展代码

2

注解 org.junit.jupiter.api.BeforeAll

执行所有容器测试之前执行的用户代码

3

接口 org.junit.jupiter.api.extension.BeforeEachCallback

在执行每个测试之前执行的扩展代码

4

注解 org.junit.jupiter.api.BeforeEach

执行每个测试之前执行的用户代码

接口 org.junit.jupiter.api.extension.BeforeTestExecutionCallback

在执行测试之前立即执行扩展代码

6

注解 org.junit.jupiter.api.Test

用户代码的实际测试方法

7

接口 org.junit.jupiter.api.extension.TestExecutionExceptionHandler

用于处理测试期间抛出的异常的扩展代码

8

接口 org.junit.jupiter.api.extension.AfterTestExecutionCallback

测试执行后立即执行扩展代码及其相应的异常处理程序

9

注解 org.junit.jupiter.api.AfterEach

执行每次测试后执行的用户代码

10

接口 org.junit.jupiter.api.extension.AfterEachCallback

执行每次测试后执行的扩展代码

11

注解 org.junit.jupiter.api.AfterAll

执行所有容器测试后执行的用户代码

12

接口 org.junit.jupiter.api.extension.AfterAllCallback

执行所有容器测试后执行的扩展代码

在最简单的情况下,仅执行实际的测试方法(步骤6); 所有其他步骤都是可选的,具体取决于用户代码的存在或相应生命周期回调的扩展支持。有关各种生命周期回调的更多详细信息,请参阅相应的JavaDoc以获取每个注释和扩展名。

从JUnit 4迁移

虽然JUnit的木星编程模型和扩展模型将不支持JUnit 4种中的功能,如RulesRunners本机,它并不期望源代码维护者将需要更新其所有现有的测试,测试扩展和自定义生成测试基础设施迁移到的JUnit木星。

相反,JUnit通过JUnit Vintage测试引擎提供了一个平缓的迁移路径,该引擎允许使用JUnit Platform基础架构执行基于JUnit 3和JUnit 4的现有测试。由于JUnit Jupiter特有的所有类和注释都位于新的org.junit.jupiter基础包下,因此在类路径中同时具有JUnit 4和JUnit Jupiter不会导致任何冲突。因此,与JUnit Jupiter测试一起保持现有的JUnit 4测试是安全的。此外,由于JUnit团队将继续为JUnit 4.x基准提供维护和错误修复版本,因此开发人员有足够的时间按照自己的时间安排迁移到JUnit Jupiter。

6.1。在JUnit平台上运行JUnit 4测试

只需确保该junit-vintage-engine工件位于测试运行时路径中。在这种情况下,JUnit 3和JUnit 4测试将由JUnit Platform启动器自动获取。

查看junit5-samples存储库中的示例项目,以了解如何使用Gradle和Maven完成此任务。

6.2。移民技巧

以下是将现有JUnit 4测试迁移到JUnit Jupiter时需要注意的事项。

  • 注释位于org.junit.jupiter.api包中。

  • 断言居住在org.junit.jupiter.api.Assertions

  • 假设驻留在org.junit.jupiter.api.Assumptions

  • @Before并且@After不再存在; 使用@BeforeEach@AfterEach替代。

  • @BeforeClass并且@AfterClass不再存在; 使用@BeforeAll@AfterAll替代。

  • @Ignore不再存在:使用@Disabled代替。

  • @Category不复存在; 使用@Tag代替。

  • @RunWith不复存在; 所取代@ExtendWith

  • @Rule并且@ClassRule不再存在; 所取代@ExtendWith有关部分规则支持,请参阅以下部分。

6.3。有限的JUnit 4规则支持

如上所述,JUnit Jupiter不会也不会本机支持JUnit 4规则。然而,JUnit团队意识到许多组织(特别是大型组织)可能拥有大量的JUnit 4代码库,包括自定义规则。为了服务这些组织并启用逐步的迁移路径,JUnit团队决定在JUnit Jupiter中逐字地支持JUnit 4规则的选择。此支持基于适配器,并且限于与JUnit Jupiter扩展模型语义兼容的那些规则,即那些不完全改变测试的整体执行流程的规则。

JUnit Jupiter目前支持以下三种规则类型,包括这些类型的子类:

  • org.junit.rules.ExternalResource(含org.junit.rules.TemporaryFolder

  • org.junit.rules.Verifier(含org.junit.rules.ErrorCollector

  • org.junit.rules.ExpectedException

与JUnit 4一样,支持规则注释字段以及方法。通过在测试类上使用这些类级别的扩展,这样在旧代码库中的规则实现可以保持不变,包括JUnit 4规则导入语句。

这种有限形式的规则支持可以通过类级注释来开启org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport此注释是由注释使所有迁移支持扩展:VerifierSupportExternalResourceSupport,和ExpectedExceptionSupport

但是,如果要开发JUnit 5的新扩展,请使用JUnit Jupiter的新扩展模型,而不是基于JUnit 4的基于规则的模型。

7.高级话题

7.1。JUnit平台启动器API

JUnit 5的突出目标之一是使JUnit与其编程客户端之间的接口(构建工具和IDE)更加强大和稳定。目的是将发现和执行测试的内部从外部所需的所有过滤和配置中分离出来。

JUnit 5引入了Launcher可用于发现,过滤和执行测试的概念。此外,第三方测试库(如Spock,Cucumber和FitNesse)可以通过提供自定义功能来插入JUnit平台的启动基础架构 TestEngine

启动API在junit-platform-launcher模块中。

启动API的一个示例消费者ConsoleLauncher在 junit-platform-console项目中。

7.1.1。发现测试

引入测试发现作为平台本身的专用功能(希望)可以免费使用IDE并构建工具,以避免过去遇到的大部分困难,以确定测试类和测试方法。

用法示例:

import static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;

import org.junit.jupiter.api.Test;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
    .selectors(
        selectPackage("com.example.mytests"),
        selectClass(MyTestClass.class)
    )
    .filters(includeClassNamePatterns(".*Test"))
    .build();

TestPlan plan = LauncherFactory.create().discover(request);

目前可以搜索类,方法,包中的所有类,甚至在类路径中搜索所有测试。发现发生在所有参与测试引擎。

所产生的测试计划基本上是适合该specification对象的所有引擎,类和测试方法的分层(和只读)描述客户端可以遍历树,检索关于节点的细节,并获取到原始源的链接(如类,方法或文件位置)。测试计划树中的每个节点都有一个唯一的ID,可以用于调用特定的测试或一组测试。

7.1.2。执行测试

执行测试有两种方法。客户端可以使用与发现阶段相同的测试规范对象,或者 - TestPlan从先前的发现步骤中加快准备对象的速度 测试进度和结果报告可以通过以下方式实现TestExecutionListener

LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
    .selectors(
        selectPackage("com.example.mytests"),
        selectClass(MyTestClass.class)
    )
    .filters(includeClassNamePatterns(".*Test"))
    .build();

Launcher launcher = LauncherFactory.create();

// Register a listener of your choice
TestExecutionListener listener = new SummaryGeneratingListener();
launcher.registerTestExecutionListeners(listener);

launcher.execute(request);

目前没有结果对象,但您可以轻松地使用侦听器将最终结果聚合到您自己的对象中。举个例子看SummaryGeneratingListener

7.1.3。插入自己的测试引擎

JUnit目前提供了两种TestEngine实现:

第三方也可以TestEngine通过在junit-platform-engine模块中实现接口注册引擎来做出贡献目前通过Java的java.util.ServiceLoader机制支持引擎注册例如, junit-jupiter-engine模块登记其org.junit.jupiter.engine.JupiterTestEngine 在一个文件名为org.junit.platform.engine.TestEngine内的/META-INF/servicesjunit-jupiter-engineJAR。

7.1.4。插入自己的测试执行侦听器

除了以Launcher编程方式注册测试执行监听器的公共API方法外,TestExecutionListener通过Java java.util.ServiceLoader工具在运行时发现的自定义实现也会自动注册DefaultLauncher例如,文件中example.TestInfoPrinter实现TestExecutionListener和声明 /META-INF/services/org.junit.platform.launcher.TestExecutionListener被自动加载和注册。

8. API进化

JUnit 5的主要目标之一是提高维护者的能力来演进JUnit,尽管它被用于许多项目。使用JUnit 4,最初添加为内部构造的很多东西只被外部扩展名作者和工具构建者所使用。这使得JUnit 4变得特别困难,有时候是不可能的。

这就是为什么JUnit 5为所有公开的接口,类和方法引入了一个定义的生命周期。

8.1。API注释

每个已发布的工件都有版本号<major>.<minor>.<patch>,所有公开的接口,类和方法都使用@API进行注释注释的Usage值可以分配以下五个值之一:

用法描述

Internal

JUnit本身以外的任何代码都不得使用。可能会被删除,恕不另行通知。

Deprecated

不应再使用 可能会在下一个次要版本中消失。

Experimental

针对新的实验功能,我们正在寻找反馈。
谨慎使用此元素; 它可能会被推广到Maintained或 Stable将来,但也可能会被删除,恕不另行通知,即使在补丁。

Maintained

适用于至少在当前主要版本的下一个次要版本中不会以向后兼容的方式更改的功能如果计划移除,将首先降级Deprecated

Stable

针对在当前主要版本(5.*)中不会以向后兼容的方式进行更改的功能

如果@API注释存在于类型上,则它也被认为适用于该类型的所有公共成员。允许成员声明不同Usage 的稳定性较低的值。

8.2。模具支持

JUnit团队计划为所有JUnit用户,扩展程序和工具构建器提供本机工具支持。工具支持将提供一种方法来检查JUnit API是否按照@API注释声明使用。

贡献者

在GitHub上直接浏览当前的贡献者列表

10.发行说明

5.0.0-ALPHA

发布日期: 2016年2月1日

范围: JUnit 5的Alpha版本

5.0.0-M1

发布日期: 2016年7月7日

范围: JUnit 5的第一个里程碑版本

变更摘要

以下是全局更改的列表。有关“平台”,“木星”和“老式”特定的更改的详细信息,请参阅以下专门的部分。有关此版本的所有已关闭问题和提取请求的完整列表,请参阅GitHub上JUnit存储库中的 5.0 M1里程碑页面。

  • 在公开的伪影JAR清单现在包含附加的元数据,例如 Created-ByBuilt-ByBuild-DateBuild-TimeBuild-Revision, Implementation-TitleImplementation-VersionImplementation-Vendor,等。

  • 已发布的工件现在包含LICENSE.mdMETA-INF

  • JUnit现在参与了Up Grab 运动的开源贡献。

  • 所有已发布工件的组ID,工件ID和版本已更改。

  • 所有基本包都已重命名。

表2.人工迁移
旧组ID旧神器ID新组ID新人工制品ID新版本

org.junit

junit-commons

org.junit.platform

junit-platform-commons

1.0.0

org.junit

junit-console

org.junit.platform

junit-platform-console

1.0.0

org.junit

junit-engine-api

org.junit.platform

junit-platform-engine

1.0.0

org.junit

junit-gradle

org.junit.platform

junit-platform-gradle-plugin

1.0.0

org.junit

junit-launcher

org.junit.platform

junit-platform-launcher

1.0.0

org.junit

junit4-runner

org.junit.platform

junit-platform-runner

1.0.0

org.junit

surefire-junit5

org.junit.platform

junit-platform-surefire-provider

1.0.0

org.junit

junit5-api

org.junit.jupiter

junit-jupiter-api

5.0.0

org.junit

junit5-engine

org.junit.jupiter

junit-jupiter-engine

5.0.0

org.junit

junit4-engine

org.junit.vintage

junit-vintage-engine

4.12.0

表3.软件包迁移
旧基本套餐新基地套餐

org.junit.gen5.api

org.junit.jupiter.api

org.junit.gen5.commons

org.junit.platform.commons

org.junit.gen5.console

org.junit.platform.console

org.junit.gen5.engine.junit4

org.junit.vintage.engine

org.junit.gen5.engine.junit5

org.junit.jupiter.engine

org.junit.gen5.engine

org.junit.platform.engine

org.junit.gen5.gradle

org.junit.platform.gradle.plugin

org.junit.gen5.junit4.runner

org.junit.platform.runner

org.junit.gen5.launcher

org.junit.platform.launcher

org.junit.gen5.launcher.main

org.junit.platform.launcher.core

org.junit.gen5.surefire

org.junit.platform.surefire.provider

JUnit平台

  • ConsoleRunner已更名为ConsoleLauncher

  • ConsoleLauncher现在在退出时总是返回状态码,并且 已经删除启用退出代码标志。

  • junit-platform-console神器不再定义传递依赖于 junit-platform-runnerjunit-jupiter-enginejunit-vintage-engine

  • JUnit5转轮已更名为JUnitPlatform

    • @Packages已更名@SelectPackages

    • @Classes已更名@SelectClasses

    • @UniqueIds 已被删除。

    • @UseTechnicalNames 已经介绍

  • JUnit平台的Gradle插件已经彻底改革。

    • JUnit Platform Gradle插件现在需要Gradle 2.5或更高版本。

    • junit5Test摇篮任务已更名为junitPlatformTest

    • junit5摇篮插件配置已更名为junitPlatform

      • runJunit4已被替换enableStandardTestTask

      • version已被替换platformVersion

    • 有关详细信息,请参阅Gradle

  • XML测试报告生成已经进行了大修。

    • XML报表现在包含换行符。

    • 特定于JUnit Platform的不符合事实标准XML模式中的标准属性的属性现在包含在元素中的CDATA块中 <system-out>

    • XML报告现在使用真正的方法名称和完全限定的类名称而不是显示名称。

  • 唯一ID TestIdentifier现在是String

  • TestSource现在用由专用的层次结构中的接口 CompositeTestSourceJavaSourceJavaPackageSourceJavaClassSource, JavaMethodSourceUriSourceFileSystemSourceDirectorySource,和 FileSource

  • 所有的DiscoverySelector工厂方法已被移动到一个新的DiscoverySelectors 类,作为所有选择方法的集中集合

  • Filter.filter()已更名Filter.apply()

  • TestTag.of()已更名TestTag.create()

  • TestDiscoveryRequest已更名LauncherDiscoveryRequest

  • TestDiscoveryRequestBuilder已更名LauncherDiscoveryRequestBuilder

  • LauncherDiscoveryRequest 现在是不变的

  • TestDescriptor.allDescendants()已更名TestDescriptor.getAllDescendants()

  • TestEngine#discover(EngineDiscoveryRequest)已被替换TestEngine#discover(EngineDiscoveryRequest, UniqueId)

  • 介绍ConfigurationParametersLauncher经用品的发动机 EngineDiscoveryRequestExecutionRequest

  • 这些ContainerLeaf抽象已经从中删除HierarchicalTestEngine

  • getName()方法已移出TestIdentifier,并TestDescriptor赞成通过检索实现特有的名称TestSource

  • 测试引擎现在被允许在本质上是完全动态的。换句话说,发现阶段TestEngine不再需要创建TestDescriptor条目 一个可任选现在在寄存器容器和动态测试执行阶段TestEngine

  • 包括和排除对引擎和标签的支持已经完全修改。

    • 引擎和标签不再需要,而是包含在内

    • ConsoleLauncher现在支持以下选项:tinclude-tag, Texclude-tage/include-engineE/exclude-engine

    • 该摇篮插件现在支持enginestags与嵌套构造块 includeexclude条目。

    • EngineFilter现在支持includeEngines()excludeEngines()工厂方法。

    • JUnitPlatform转轮现在支持@IncludeTags@ExcludeTags, @IncludeEngines,和@ExcludeEngines

JUnit木星

  • junit5引擎ID已重命名为junit-jupiter

  • JUnit5TestEngine已更名JupiterTestEngine

  • Assertions 现在提供以下支持:

    • assertEquals() 为原始类型

    • assertEquals() 双打和三角洲浮动

    • assertArrayEquals()

    • 现在提供预期和实际值AssertionFailedError

  • 动态测试:现在可以通过lambda表达式在运行时动态注册测试。

  • TestInfo现在提供了对标签的访问getTags()

  • @AfterEach方法和如果异常是由抛出回调现在援引@Test法,@BeforeEach方法,或之前回调。

  • @AfterAll方法和所有回调现在被保证被调用。

  • 重复注释,比如@ExtendWith@Tag现在在测试类层次结构中,以及在接口上超发现。

  • 扩展程序现在在测试类或接口层次结构中自动向上注册

  • 现在可以禁用测试和容器执行条件

  • InstancePostProcessor已更名TestInstancePostProcessor

    • TestInstancePostProcessor实现现在可以在@Nested 测试类层次结构中正确应用

  • MethodParameterResolver已更名ParameterResolver

    • ParameterResolver现在的API是基于java.lang.reflect.Executable,因此可以用来解析方法的参数构造。

    • ParameterContext传递给supports()扩展的resolve()方法 ParameterResolver

    • ParameterResolver扩展名现在支持原始类型的解析

  • ExtensionPointRegistryExtensionRegistrar已经宣布支持通过声明的注册被删除@ExtendWith

  • BeforeAllExtensionPoint已更名BeforeAllCallback

  • AfterAllExtensionPoint已更名AfterAllCallback

  • BeforeEachExtensionPoint已更名BeforeEachCallback

  • BeforeAllExtensionPoint已更名BeforeAllCallback

  • 新增BeforeTestExecutionCallbackAfterTestExecutionCallback扩展API。

  • ExceptionHandlerExtensionPoint已更名TestExecutionExceptionHandler

  • 测试例外现在通过扩展程序提供TestExtensionContext

  • ExtensionContext.Store 现在支持其许多方法的类型安全变体。

  • ExtensionContext.getElement()现在返回Optional

  • Namespace.of()已更名Namespace.create()

  • TestInfo并且ExtensionContext有新getTestClass()getTestMethod() 方法。

  • getName()方法已经通过当前测试类或测试方法有意的检索上下文特定名称中移除TestInfoExtensionContext有利于此。

JUnit复古

  • junit4引擎ID已重命名为junit-vintage

  • JUnit4TestEngine已更名VintageTestEngine

5.0.0-M2

发布日期: 2016年7月23日

范围: JUnit 5的第二个里程碑版本

变更摘要

此版本主要是自从发现以来发现的错误的修补程序版本5.0.0-M1

以下是全局更改的列表。有关“平台”,“木星”和“老式”特定的更改的详细信息,请参阅以下专门的部分。有关此版本的所有已关闭问题和提取请求的完整列表,请参阅GitHub上JUnit存储库中的 5.0 M2里程碑页面。

  • JUnit 5 Gradle构建现在在Microsoft Windows上正常运行。

  • 在AppVeyor上已经建立了针对Microsoft Windows的持续集成。

JUnit平台

Bug修复
  • 容器中的故障(例如@BeforeAll抛出异常的方法)现在在使用ConsoleLauncher或JUnit Platform Gradle插件时会失败

  • JUnit Platform Surefire Provider不再静默地忽略纯粹的动态测试类 - 例如,只能声明@TestFactory方法的测试类

  • junit-platform-consolejunit-platform-console.bat包含在shell脚本junit-platform-console-<release version>TAR和ZIP分布现在可以正确指ConsoleLauncher代替ConsoleRunner

  • TestExecutionSummary由所使用的ConsoleLauncher和JUnit平台摇篮插件现在包括对故障的实际的异常类型。

  • 现在,针对在类加载和处理期间遇到的异常,例如在处理具有格式不正确名称的类时,将进行类路径扫描。底层异常被吞没并记录与违规的文件路径。如果异常是一个列入黑名单的异常,例如OutOfMemoryError,它将被重新抛出。

弃用
  • 通用基于名称的发现选择(即selectName()selectNames())在 DiscoverySelectors赞成的专用已弃用 selectPackage(String)selectClass(String)selectMethod(String)方法。

新功能
  • 支持选择完全限定方法名称的新 selectMethod(String)方法DiscoverySelectors

JUnit木星

Bug修复
  • @ExtendWith在类级别和方法级别声明的扩展实现将不再被多次注册。

JUnit复古

5.0.0-M1之后没有变化

5.0.0-M3

发布日期: 2016年11月30日

范围: JUnit 5的第三个里程碑发布,重点是JUnit 4互操作性,其他发现选择器和文档。

有关此版本的所有已关闭问题和提取请求的完整列表,请参阅GitHub上JUnit存储库中的 5.0 M3里程碑页面。

JUnit平台

Bug修复
  • ColoredPrintingTestListener,它ConsoleLauncher现在在打印异常消息时输出实际的异常类型和堆栈跟踪。

  • 默认包中的测试类现在通过类路径扫描在扫描类路径时被拾取 - 例如,当未选择显式包时与JUnit Platform Gradle插件结合使用。

  • 类路径扫描不再加载类名称过滤器排除的类。

  • Classpath扫描不再尝试加载Java 9 module-info.class文件。

废弃和破坏变化
  • ClasspathSelector已更名为ClasspathRootSelector避免混淆ClasspathResourceSelector

  • JavaPackageSource已被重命名为PackageSource与之对齐PackageSelector

  • JavaClassSource已被重命名为ClassSource与之对齐ClassSelector

  • JavaMethodSource已被重命名为MethodSource与之对齐MethodSelector

  • PackageSourceClassSourceMethodSource现在直接实现TestSource 接口,而不是JavaSource它已被删除接口。

  • 通用基于名称的发现选择(即selectName()selectNames())在 DiscoverySelectors赞成的专用已弃用 selectPackage(String)selectClass(String)selectMethod(String)方法。

  • ClassFilter已经被重命名ClassNameFilter并且现在实现,DiscoveryFilter<String> 而不是DiscoveryFilter<Class<?>>在类路径扫描中加载类之前可以应用它。

  • ClassNameFilter.includeClassNamePattern现在已经不赞成了ClassNameFilter.includeClassNamePatterns

  • @IncludeClassNamePattern现在已经不赞成了@IncludeClassNamePatterns

  • -p用于配置其他类路径条目命令行选项 ConsoleLauncher已被重命名-cp,以便与标准java可执行文件的选项名称对齐此外,--class-path还引入了一个新的别名,而现有的--classpath命令行选项保持不变。

  • -a--all命令行选项ConsoleLauncher已更名为--scan-class-path

  • --xml-reports-dir该命令行选项ConsoleLauncher已更名为--reports-dir

  • -C-D-r用于短命令行选项ConsoleLauncher已经赞成使用他们的的被去除当量--disable-ansi-colors, --hide-details--reports-dir分别。

  • ConsoleLauncher不再支持通过非选项参数。请使用新的显式选择器选项来指定包,类或方法名称。或者, --scan-class-path现在接受可选参数,可以用于选择要扫描的类路径条目。可以通过使用系统的路径分隔符(;在Windows :上,Unix上)分离多个类路径条目

  • LauncherDiscoveryRequestBuilder不再接受null对值selectorsfilters或配置参数的地图。

  • Gradle插件配置中的类名,引擎ID和标记的过滤器现在需要包装在filters扩展元素中(请参阅配置过滤器)。

新功能
  • 用于选择URI的selectUri(…​)方法DiscoverySelectorsTestEngine 可以通过查询注册的实例来检索这些值UriSelector

  • 用于在文件系统中选择文件和目录的方法selectFile(…​)selectDirectory(…​)方法DiscoverySelectors一个TestEngine可以通过查询注册的情况下检索此类值FileSelectorDirectorySelector

  • 用于按名称选择 类路径资源(如XML或JSON文件)的selectClasspathResource(String)方法,其中名称是当前类路径中资源的 分隔路径名。可以通过查询注册的实例来检索这些值此外,一类路径的资源可以提供作为一个 通过新DiscoverySelectors/TestEngineClasspathResourceSelectorTestSourceTestIdentifierClasspathResourceSource

  • 现在,selectMethod(String)方法DiscoverySelectors支持选择接受参数的方法完全限定方法名称 - 例如"org.example.TestClass#testMethod(org.junit.jupiter.api.TestInfo)"

  • TestExecutionSummary使用的ConsoleLauncher和JUnit平台摇篮插件现在包括除了测试赛所有容器事件的统计数据。

  • TestExecutionSummary现在可以用来获取所有故障的列表。

  • 现在ConsoleLauncher,Gradle插件和JUnitPlatform转轮 ^.*Tests?$用作类名的默认模式,以包含在测试运行中。

  • Gradle插件现在允许显式选择应该执行哪些测试(请参阅配置选择器)。

  • @Testable注释,TestEngine实现可以用于向IDE和工具供应商发出注释或元注释元素是可测试的(即它可以作为JUnit平台上的测试执行)。

  • 现在可以传递使用OR语义组合的多个正则表达式ClassNameFilter.includeClassNamePatterns

  • 使用OR语义组合的多个正则表达式现在可以传递 @IncludeClassNamePatternsJUnitPlatform转轮。

  • 现在可以将使用OR语义组合的多个正则表达式传递给JUnit Platform Gradle插件(请参阅配置过滤器)和ConsoleLauncher (请参阅选项)。

  • 现在可以使用PackageNameFilter.includePackageNames 或包含或排除软件包名称PackageNameFilter.excludePackageNames

  • 现在可以使用@IncludePackages@ExcludePackages 与JUnitPlatform赛跑者组合来包括或排除软件包名称

  • 软件包名称现在可以通过过滤器配置包含或排除到JUnit Platform Gradle插件(请参阅配置过滤器)和ConsoleLauncher (请参阅选项)。

  • junit-platform-console不再对托管的依赖 JOPT简单因此,现在可以测试使用该库的不同版本的自定义代码。

  • 程序包选择器的解析现在也扫描JAR文件。

  • Surefire提供商现在支持分叉。

  • Surefire提供商现在支持使用以下参数之一进行过滤:

    • 包括:groups/includeTags

    • 排除:excludedGroups/excludeTags

  • Surefire提供商现已获得Apache License v2.0许可。

JUnit木星

Bug修复
  • @AfterEach方法现在在测试类层次结构中用自下而上的语义来执行

  • DynamicTest.stream()现在接受一个ThrowingConsumer传统 Consumer测试执行器,从而允许定制的动态测试流可能会抛出检查异常。

  • 现在在测试方法级别注册的扩展名用于相应测试方法的调用 @BeforeEach@AfterEach方法。

  • JupiterTestEngine现在经由用于接受阵列或原语类型作为参数的方法的独特ID支持的测试方法的选择。

  • ExtensionContext.Store 现在是线程安全的。

废弃和破坏变化
  • Executable功能接口已搬迁到新的专用 org.junit.jupiter.api.function包。

  • Assertions.expectThrows()已被弃用赞成Assertions.assertThrows()

新功能和改进
  • 支持懒惰和抢先超时的lambda表达式Assertions有关详细信息AssertionsDemo,请参阅org.junit.jupiter.AssertionsJavadoc中的示例

  • assertIterableEquals()检查两个Iterable的断言是否相当(详见Javadoc)。

  • 它的新变体Assertions.assertAll()接受可执行文件流(即 Stream<Executable>)。

  • Assertions.assertThrows() 现在返回抛出的异常。

  • @BeforeAll并且@AfterAll现在可以在接口中的静态方法上声明。

  • JUnit的4条规则org.junit.rules.ExternalResourceorg.junit.rules.Verifierorg.junit.rules.ExpectedException包括子类现在支持JUnit的木星,方便的JUnit 4代码库的迁移。

JUnit复古

5.0.0-M2之后没有变化

5.0.0-M4

发布日期: 2017年4月1日

范围: JUnit 5的第四个里程碑发布,重点是测试模板,重复测试和参数化测试。

有关此版本的所有已关闭问题和提取请求的完整列表,请参阅GitHub上JUnit存储库中的 5.0 M4里程碑页面。

JUnit平台

Bug修复
  • JUnit Platform Gradle插件现在将其依赖关系添加到固定版本(与插件版本相同),而不是1.+默认情况下的动态版本控制方案(was )以确保可重复构建。

  • JUnit Platform Gradle插件现在显式地应用了Gradle内置的java插件,因为它具有应用的隐式依赖性。

  • 所有findMethods()实现ReflectionUtils不再返回合成方法。阴影覆盖相等的 方法也不再包含在结果中。

  • 介绍getLegacyReportingName()TestIdentifierTestDescriptor这允许JUnit Platform Gradle Plugin和Surefire Provider通过getLegacyReportingName()而不是使用源位置来解析类和方法名称

  • %20现在,在它们的路径()中包含空格的JAR文件在被用于类路径扫描例程之前被正确解码。

  • 更新findNestedClasses()ReflectionUtils以便搜索嵌套类也返回继承的嵌套类(无论它们是否是静态的)。

废弃和破坏变化
  • 软件包中的所有测试套件注释org.junit.platform.runner已被移动到org.junit.platform.suite.apijunit-platform-suite-api 模块中软件包这包括注释,比如@SelectClasses@SelectPackages

  • 删除过时selectNames()的基于域名的发现选择 DiscoverySelectors赞成专用selectPackage(String), selectClass(String)selectMethod(String)方法。

  • ClassNameFilter.includeClassNamePattern去掉了; ClassNameFilter.includeClassNamePatterns改用。

  • @IncludeClassNamePattern已被删除; @IncludeClassNamePatterns改用。

  • --hide-details选项已ConsoleLauncher被弃用; 使用--details none代替。

  • 不再提供包含控制台启动器的ZIP分发包。它已被一个可执行的独立JAR分发代替。有关详细信息,请参阅下面的“新功能”部分。

  • 这个MethodSortOrder枚举ReflectionUtils已经重命名了HierarchyTraversalMode那些受影响的人现在应该使用ReflectionSupport而不是ReflectionUtils

  • 该方法execute(LauncherDiscoveryRequest launcherDiscoveryRequest)Launcher已被弃用,并将在里程碑M5被移除。而是使用以下新的方法来注册TestExecutionListener除了已经注册的监听器之外的所提供的S,但是仅提供LauncherDiscoveryRequestexecute(LauncherDiscoveryRequest launcherDiscoveryRequest, TestExecutionListener…​ listeners)

新功能和改进
  • TestExecutionListener现在可以通过Java的ServiceLoader机制自动注册自定义实现

  • 新的getGroupId()getArtifactId()getVersion()默认方法在 TestEngineAPI中进行调试和报告。默认情况下,包属性(通常来自JAR清单属性)用于确定工件ID和版本; 而默认情况下,组ID为空。有关TestEngine详细信息,请咨询Javadoc 

  • 已发现的测试引擎的日志记录信息已被增强,包括组ID,工件ID和每个测试引擎的版本(如果可以通过getGroupId()) getArtifactId(),和和getVersion()方法。

  • 现在的--scan-classpath选项ConsoleLauncher允许在作为显式参数提供测试时扫描JAR文件(请参阅选项)。

  • 现在的新--details <Details>选项ConsoleLauncher允许在执行测试时选择输出详细信息模式。使用的一个:noneflattreeverbose如果 none被选中,则仅显示汇总和测试失败(参见 选项)。

  • 现在的新--details-theme <Theme>选项ConsoleLauncher允许在执行测试并打印为树时选择一个主题。使用以下之一:asciiunicode (参见选项)。

  • junit-platform-console-standalone-1.0.0-M5.jar包含具有所有依赖关系的Jupiter和Vintage测试引擎的可执行工件由默认构建过程生成,存储在Maven Central中junit-platform-console-standalone/build/libs并发布到 Maven Central它提供了JUnit 5在手动管理其依赖性的项目中的麻烦使用,类似于 JUnit 4中已知的 简单JAR

  • New --exclude-classname--N)选项添加到ConsoleLauncher接受正则表达式以排除完全限定名称匹配的类。当重复此选项时,所有模式将使用OR语义进行组合。

  • 新的JUnit Platform支持包org.junit.platform.commons.support,其中包含 用于注释,反射和类路径扫描任务的维护实用程序方法。 TestEngineExtension鼓励作者使用这些支持的方法,以便与JUnit平台的行为保持一致。

  • 压缩的逻辑背后TestDescriptor.isTest()TestDescriptor.isContainer()从两个独立的boolean属性到一个枚举,即TestDescriptor.Type有关详细信息,请参阅下一个项目符号。

  • 引入TestDescriptor.Type枚举与TestDescriptor.getType()访问器定义所有可能的描述符类型。TestDescriptor.isTest()TestDescriptor.isContainer() 现在委托TestDescriptor.Type的常量。

  • 引入TestDescriptor.prune()TestDescriptor.pruneTree()允许引擎作者定制在JUnit平台触发修剪时会发生什么。

  • TestIdentifier现在使用新的TestDescriptor.Type枚举来存储底层类型。它可以通过新的TestIdentifier.getType()方法检索此外,TestIdentifier.isTest()TestIdentifier.isContainer()现在委托TestDescriptor.Type的常量。

JUnit木星

Bug修复
  • 修复了通过方法选择器选择时阻止在同一个类中发现两个或多个方法的错误。

  • @Nested 当在超类中声明时,现在将检测非静态测试类。

  • 当在类层次结构中的多个级别声明时,现在强制执行覆盖@BeforeEach@AfterEach方法的正确执行顺序它现在总是 super.beforethis.beforethis.testthis.after,和super.after,即使编译器添加的合成方法。

  • TestExecutionExceptionHandler现在被调用的方式与他们注册的相反顺序类似于所有其他的“  ”扩展。

废弃和破坏变化
  • 删除已弃用的Assertions.expectThrows()方法Assertions.assertThrows()

  • ExtensionContext.Namespace由相同的部分组成但不同的顺序不再被认为是相等的。

新功能和改进
  • 通过新注释参数化测试进行一流的支持@ParameterizedTest有关详细信息,请参阅参数化测试

  • 通过新的注释和 API 重复测试的一流支持有关详细信息,请参阅重复测试@RepeatedTestRepetitionInfo

  • 引入@TestTemplate注释和附带的扩展点 TestTemplateInvocationContextProvider

  • Assertions.assertThrows() 当生成断言失败消息时,现在使用规范名称作为异常类型。

  • TestInstancePostProcessor现在调用了在测试方法上注册的。

  • 新的变体Assertions.failAssertions.fail(Throwable cause)Assertions.fail(String message, Throwable cause)

  • 新的Assertions.assertLinesMatch()比较字符串列表,特征Object::equals 和正则表达式检查。assertLinesMatch()还提供了一种快速机制来跳过预期在每次调用中改变的行 - 例如持续时间,时间戳,堆栈跟踪等。有关org.junit.jupiter.Assertions详细信息,请参阅JavaDoc 

  • 扩展程序现在可以通过Java的ServiceLoader机制自动注册请参阅自动分机注册

JUnit复古

Bug修复
  • 修复了仅导致测试报告失败的错误。例如,使用 ErrorCollector规则时,只报告最后一次失败的检查。现在,所有的故障报告都使用了org.opentest4j.MultipleFailuresError

5.0.0-M5

发布日期: 2017年7月4日

范围: JUnit 5的第五个里程碑版本,重点是动态容器,测试实例生命周期管理和次要的API更改。

  这是一个里程碑版本,包含突破性的变化。请参阅上述 说明使用此版本的IntelliJ IDEA版本,捆绑较旧的里程碑版本。

有关此版本的所有已关闭问题和提取请求的完整列表,请参阅GitHub上的JUnit存储库中的 5.0 M5里程碑页面。

  • 所有已发布的JAR工件现在包含一个Automatic-Module-Name清单属性,其值用作该JAR文件放置在Java 9模块路径上时定义的自动模块的名称。

表4.自动模块名称
JAR文件自动模块名称

junit-jupiter-api-<VERSION>.jar

org.junit.jupiter.api

junit-jupiter-engine-<VERSION>.jar

org.junit.jupiter.engine

junit-jupiter-migrationsupport-<VERSION>.jar

org.junit.jupiter.migrationsupport

junit-jupiter-params-<VERSION>.jar

org.junit.jupiter.params

junit-platform-commons-<VERSION>.jar

org.junit.platform.commons

junit-platform-console-<VERSION>.jar

org.junit.platform.console

junit-platform-engine-<VERSION>.jar

org.junit.platform.engine

junit-platform-gradle-plugin-<VERSION>.jar

org.junit.platform.gradle.plugin

junit-platform-launcher-<VERSION>.jar

org.junit.platform.launcher

junit-platform-runner-<VERSION>.jar

org.junit.platform.runner

junit-platform-suite-api-<VERSION>.jar

org.junit.platform.suite.api

junit-platform-surefire-provider-<VERSION>.jar

org.junit.platform.surefire.provider

junit-vintage-engine-<VERSION>.jar

org.junit.vintage.engine

JUnit平台

Bug修复
  • MethodSelector.getMethodParameterTypes()null如果通过DiscoverySelectors没有显式参数类型创建选择器,参数类型不再返回具体来说,如果不提供参数类型,不能推导出一个空字符串。类似地,如果参数类型未明确提供但可以被推导(例如,通过提供的Method引用),getMethodParameterTypes()现在返回一个包含推导参数类型的字符串。

  • 而不是抛出一个的NullPointerExceptionDetails.TREE模式中, ConsoleLauncher现在打印相应抛出的toString()作为后备机制表示。

  • DefaultLauncher现在捕获并警告引擎在其发现和执行阶段生成的异常。其他发动机正常加工。

  • UniqueId.Segment当生成唯一ID的字符串表示时,类型和值字符串现在将部分进行URL编码。通过将活性保留所有字符UniqueIdFormat的语法(例如,[:],和/)进行编码。默认解析器也已更新,以对这些编码段进行解码。

废弃和破坏变化
  • 已弃用的--hide-details选项ConsoleLauncher已被删除; 使用 --details none代替。

  • 以前已弃用以下方法已被删除。

    • junit-platform-engine: Node.execute(EngineExecutionContext)

    • junit-platform-commons: ReflectionUtils.findAllClassesInClasspathRoot(Path, Predicate, Predicate)

  • 接口isLeaf()方法org.junit.platform.engine.support.hierarchical.Node已被删除。

  • 默认的方法pruneTree(),并hasTests()已经从删除 TestDescriptor

新功能和改进
  • 当使用DiscoverySelectors通过其完全限定方法名称选择方法 或通过提供methodParameterTypes逗号分隔的字符串时,现在可以使用源代码语法(int[]原始数组和java.lang.String[]对象数组)来描述数组参数类型此外,现在有可能使用任一JVM的内部字符串表示(例如,描述多维数组类型[[[Iint[][][][[Ljava.lang.String;java.lang.String[][]等)或源代码的语法(例如,boolean[][][], java.lang.Double[][]等等)。

  • junitPlatformTest现在可以在构建的配置阶段直接访问JUnit Platform Gradle插件任务

  • JUnit Platform Gradle插件现在播放了与Gradle Kotlin DSL很好。

  • TestEngine通过Java ServiceLoader机制发现a时,现在将尝试确定引擎类加载的位置,如果位置URL可用,则将在配置级别进行记录。

  • mayRegisterTests()方法可用于表示TestDescriptor在执行期间将注册动态测试。

  • TestPlan现在可以用于查询是否containsTests()Surefire提供程序使用它来决定一个类是否是应该执行的测试类。

  • ENGINE枚数常数从中除去TestDescriptor.Type现在的默认类型EngineDescriptorTestDescriptor.Type.CONTAINER

JUnit木星

Bug修复
  • @ParameterizedTest 不再为生命周期方法和测试类构造函数解析参数,以提高与使用不同参数列表的常规和参数化测试方法的互操作性。

废弃和破坏变化
  • 迁移支持模块现在被命名junit-jupiter-migrationsupport,没有破折号-之间migrationsupport

  • 为了确保JUnit Jupiter中所有支持的扩展API 可组合性,现有API中的几种方法已被重命名。

  • arguments()该方法中ArgumentsProvider的在API junit-jupiter-params 模块已更名为provideArguments()

  • 模块中ObjectArrayArgumentsjunit-jupiter-params已被删除; Arguments现在可以通过Arguments.of(…​)静态工厂方法获得创建实例 的功能

  • names物业@MethodSource已被重命名为value

  • API中getTestInstance()方法TestExtensionContext已被移动到 ExtensionContextAPI中。此外,签名已经从 Object getTestInstance()Optional<Object> getTestInstance()

  • API中getTestException()方法TestExtensionContext已被移动到 ExtensionContextAPI并重命名为getExecutionException()

  • TestExtensionContextContainerExtensionContext接口已被删除,并且所有Extension接口已经更改为使用ExtensionContext代替。

  • TestExecutionConditionContainerExecutionCondition已被替换为用于条件测试执行的单个通用扩展API ExecutionCondition

表5.扩展API迁移
扩展API旧名称新名称/位置

ParameterResolver

supports()

supportsParameter()

ParameterResolver

resolve()

resolveParameter()

ContainerExecutionCondition

evaluate()

evaluateExecutionCondition() 在 ExecutionCondition

TestExecutionCondition

evaluate()

evaluateExecutionCondition() 在 ExecutionCondition

TestExtensionContext

getTestException()

getExecutionException() 在 ExtensionContext

TestExtensionContext

getTestInstance()

getTestInstance() 在 ExtensionContext

TestTemplateInvocationContextProvider

supports()

supportsTestTemplate()

TestTemplateInvocationContextProvider

provide()

provideTestTemplateInvocationContexts()

新功能和改进
  • 测试实例生命周期现在可以通过新的类级@TestInstance注释从默认的每方法模式切换到新的每类模式这样可以在给定测试类中的测试方法之间以及测试类中的非静态@BeforeAll@AfterAll方法之间共享测试实例状态

  • @BeforeAll@AfterAll方法都不再需要是static,如果测试类与注释@TestInstance(Lifecycle.PER_CLASS)这样可以实现以下新功能。

    • 测试类的声明@BeforeAll@AfterAll方法@Nested

    • 接口方法的声明@BeforeAll@AfterAll接口default

    • 使用Kotlin编程语言实现的测试类中的简化声明@BeforeAll@AfterAll方法。

  • Assertions.assertAll()现在跟踪任何类型的异常(而不是仅跟踪类型AssertionError的异常),除非异常是列入黑名单的异常,在这种情况下,它将被立即重新引导。

  • 如果a @ParameterizedTest接受一个数组作为参数,则当为参数化测试的调用生成显示名称时,数组的字符串表示形式现在将被转换为人类可读的格式。

  • @EnumSource现在提供用于控制提供的名称如何解释一个枚举常数选择模式。支持的模式包括INCLUDEEXCLUDE 以及正则表达式模式匹配模式MATCH_ALLMATCH_ANY

  • 扩展程序现在可以通过使用Store新引入的引擎级别来共享顶级测试类的状态ExtensionContext

  • 参考使用论证提供方法@MethodSource现在可以返回的情况下 DoubleStreamIntStreamLongStream直接。

  • @TestFactory现在支持任意嵌套的动态容器。有关详细信息,请参阅DynamicContainer 和抽象基类DynamicNode

  • ExtensionContext.getExecutionException()现在提供抛出的异常@BeforeAll 的方法或BeforeAllCallbacksAfterAllCallbacks

JUnit复古

Bug修复
  • VintageTestEngine不再过滤掉声明为静态成员类的测试类,因为它们是有效的JUnit 4测试类。

  • VintageTestEngine不再尝试执行抽象类的测试类。相反,现在会记录一个警告,指出排除这些类。