Migrating from JUnit 4 to JUnit 5 – 从JUnit 4迁移到JUnit 5

最后修改: 2017年 7月 16日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll learn how to migrate from JUnit 4 to the latest JUnit 5 release, with an overview of the differences between the two versions of the library.

在本教程中,我们将学习如何从JUnit 4迁移到最新的JUnit 5版本,并概述两个版本的库之间的差异。

For the general guidelines on using JUnit 5, see our article here.

关于使用JUnit 5的一般准则,请参阅我们的文章这里

2. JUnit 5 Advantages

2.JUnit 5的优势

Let’s start with the previous version, JUnit 4 ,which has some clear limitations:

让我们从以前的版本开始,JUnit 4 ,它有一些明显的限制。

  • A single jar library contains the entire framework. We need to import the whole library, even when we only require a particular feature. In JUnit 5, we get more granularity, and can import only what’s necessary.
  • Only one test runner can execute tests at a time in JUnit 4 (e.g. SpringJUnit4ClassRunner or Parameterized ). JUnit 5 allows multiple runners to work simultaneously.
  • JUnit 4 never advanced beyond Java 7, missing out on a lot of features from Java 8. JUnit 5 makes good use of the Java 8 features.

The idea behind JUnit 5 was to completely rewrite JUnit 4 in order to negate most of these drawbacks.

JUnit 5的想法是完全重写JUnit 4,以否定这些缺点中的大部分。

3. Differences

3.差异

JUnit 4 was divided into the modules that comprise JUnit 5:

JUnit 4被划分为构成JUnit 5的模块。

  • JUnit Platform – this module scopes all the extension frameworks we might have an interest in: test execution, discovery, and reporting.
  • JUnit Vintage – this module allows backward compatibility with JUnit 4 or even JUnit 3.

3.1. Annotations

3.1.注释

JUnit 5 comes with important changes to its annotations. The most important one is that we can no longer use the @Test annotation for specifying expectations.

JUnit 5对其注解进行了重要修改。最重要的一点是,我们不能再使用@Test注解来指定期望。

The expected parameter in JUnit 4:

JUnit 4中的预期参数。

@Test(expected = Exception.class)
public void shouldRaiseAnException() throws Exception {
    // ...
}

Now we can use the method assertThrows:

现在我们可以使用assertThrows方法。

public void shouldRaiseAnException() throws Exception {
    Assertions.assertThrows(Exception.class, () -> {
        //...
    });
}

The timeout attribute in JUnit 4:

JUnit 4中的timeout属性。

@Test(timeout = 1)
public void shouldFailBecauseTimeout() throws InterruptedException {
    Thread.sleep(10);
}

Now the assertTimeout method in JUnit 5:

现在JUnit 5中的assertTimeout方法。

@Test
public void shouldFailBecauseTimeout() throws InterruptedException {
    Assertions.assertTimeout(Duration.ofMillis(1), () -> Thread.sleep(10));
}

Here are some other annotations that were changed within JUnit 5:

下面是在JUnit 5中被改变的一些其他注解。

  • @Before annotation is now @BeforeEach
  • @After annotation is now @AfterEach
  • @BeforeClass annotation is now @BeforeAll
  • @AfterClass annotation is now @AfterAll
  • @Ignore annotation is now @Disabled

3.2. Assertions

3.2.断言

We can also write assertion messages in a lambda in JUnit 5, allowing the lazy evaluation to skip complex message construction until needed:

在JUnit 5中,我们也可以在lambda中编写断言消息,允许懒人评估跳过复杂的消息构造,直到需要为止。

@Test
public void shouldFailBecauseTheNumbersAreNotEqual_lazyEvaluation() {
    Assertions.assertTrue(
      2 == 3, 
      () -> "Numbers " + 2 + " and " + 3 + " are not equal!");
}

Furthermore, we can group assertions in JUnit 5:

此外,我们可以在JUnit 5中对断言进行分组。

@Test
public void shouldAssertAllTheGroup() {
    List<Integer> list = Arrays.asList(1, 2, 4);
    Assertions.assertAll("List is not incremental",
        () -> Assertions.assertEquals(list.get(0).intValue(), 1),
        () -> Assertions.assertEquals(list.get(1).intValue(), 2),
        () -> Assertions.assertEquals(list.get(2).intValue(), 3));
}

3.3. Assumptions

3.3.假设

The new Assumptions class is now in org.junit.jupiter.api.Assumptions. JUnit 5 fully supports the existing assumptions methods in JUnit 4, and also adds a new set of methods allowing us to run some assertions under specific scenarios only:

新的Assumptions类现在在org.junit.jupiter.api.Assumptions。JUnit 5完全支持JUnit 4中现有的假设方法,还增加了一组新的方法,允许我们只在特定场景下运行一些断言。

@Test
public void whenEnvironmentIsWeb_thenUrlsShouldStartWithHttp() {
    assumingThat("WEB".equals(System.getenv("ENV")),
      () -> {
          assertTrue("http".startsWith(address));
      });
}

3.4. Tagging and Filtering

3.4.标记和过滤

In JUnit 4, we could group tests by using the @Category annotation. In JUnit 5, the @Category annotation is replaced by the @Tag annotation:

在JUnit 4中,我们可以通过使用@Category注解来分组测试。在JUnit 5中,@Category注解被@Tag注解所取代。

@Tag("annotations")
@Tag("junit5")
public class AnnotationTestExampleTest {
    /*...*/
}

We can include/exclude particular tags using the maven-surefire-plugin:

我们可以使用maven-surefire-plugin包含/排除特定的标签。

<build>
    <plugins>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <properties>
                    <includeTags>junit5</includeTags>
                </properties>
            </configuration>
        </plugin>
    </plugins>
</build>

3.5. New Annotations for Running Tests

3.5.运行测试的新注解

In JUnit 4, we used the @RunWith annotation to integrate the test context with other frameworks, or to change the overall execution flow in the test cases.

在JUnit 4中,我们使用@RunWith 注解将测试上下文与其他框架集成,或改变测试案例的整体执行流程。

With JUnit 5, we can now use the @ExtendWith annotation to provide similar functionality.

通过JUnit 5,我们现在可以使用@ExtendWith注解来提供类似的功能。

As an example, to use the Spring features in JUnit 4:

作为一个例子,要使用JUnit 4中的Spring功能。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  {"/app-config.xml", "/test-data-access-config.xml"})
public class SpringExtensionTest {
    /*...*/
}

In JUnit 5, it’s a simple extension:

在JUnit 5中,它是一个简单的扩展。

@ExtendWith(SpringExtension.class)
@ContextConfiguration(
  { "/app-config.xml", "/test-data-access-config.xml" })
public class SpringExtensionTest {
    /*...*/
}

3.6. New Test Rules Annotations

3.6.新的测试规则注解

In JUnit 4, we used the @Rule and @ClassRule annotations to add special functionality to tests.

在JUnit 4中,我们使用@Rule和@ClassRule注解来给测试添加特殊功能。

In JUnit 5. we can reproduce the same logic using the @ExtendWith annotation.

在JUnit 5中,我们可以使用@ExtendWith注解来重现同样的逻辑。

For example, say we have a custom rule in JUnit 4 to write log traces before and after a test:

例如,假设我们在JUnit 4中有一个自定义规则,在测试前后写日志追踪。

public class TraceUnitTestRule implements TestRule {
 
    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                // Before and after an evaluation tracing here 
                ...
            }
        };
    }
}

And we implement it in a test suite:

并且我们在测试套件中实现它。

@Rule
public TraceUnitTestRule traceRuleTests = new TraceUnitTestRule();

In JUnit 5, we can write the same in a much more intuitive manner:

在JUnit 5中,我们可以用更直观的方式来写同样的内容。

public class TraceUnitExtension implements AfterEachCallback, BeforeEachCallback {

    @Override
    public void beforeEach(TestExtensionContext context) throws Exception {
        // ...
    }

    @Override
    public void afterEach(TestExtensionContext context) throws Exception {
        // ...
    }
}

Using JUnit 5’s AfterEachCallback and BeforeEachCallback interfaces, available in the org.junit.jupiter.api.extension package, we can easily implement this rule in the test suite:

使用JUnit 5的AfterEachCallbackBeforeEachCallback接口(可在org.junit.jupiter.api.extension包中获得),我们可以在测试套件中轻松实现这一规则。

@ExtendWith(TraceUnitExtension.class)
public class RuleExampleTest {
 
    @Test
    public void whenTracingTests() {
        /*...*/
    }
}

3.7. JUnit 5 Vintage

3.7.JUnit 5复制品

JUnit Vintage aids in the migration of JUnit tests by running JUnit 3 or JUnit 4 tests within the JUnit 5 context.

JUnit Vintage通过在JUnit 5上下文中运行JUnit 3或JUnit 4测试来帮助JUnit测试的迁移。

We can use it by importing the JUnit Vintage Engine:

我们可以通过导入JUnit Vintage Engine来使用它。

<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>${junit5.vintage.version}</version>
    <scope>test</scope>
</dependency>

4. Conclusion

4.总结

JUnit 5 is a modular and modern take on the JUnit 4 framework. In this article, we introduced the major differences between these two versions, and hinted at how to migrate from one to another.

JUnit 5是对JUnit 4框架的一种模块化和现代化。在这篇文章中,我们介绍了这两个版本的主要区别,并暗示了如何从一个版本迁移到另一个版本。

The full implementation of this article can be found over on GitHub.

本文的完整实现可以在GitHub上找到over