Concurrent Test Execution in Spring 5 – Spring 5中的并发测试执行

最后修改: 2017年 3月 10日

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

1. Introduction

1.介绍

Starting with JUnit 4, tests can be run in parallel to gain speed for larger suites. The problem was concurrent test execution was not fully supported by the Spring TestContext Framework prior to Spring 5.

JUnit 4开始,测试可以并行运行,以提高大型套件的速度。问题是,在Spring 5之前,Spring TestContext Framework不完全支持并发测试执行。

In this quick article, we’ll show how to use Spring 5 to run our tests in Spring projects concurrently.

在这篇快速文章中,我们将展示如何使用Spring 5Spring项目中并发运行我们的测试

2. Maven Setup

2.Maven的设置

As a reminder, to run JUnit tests in parallel, we need to configure the maven-surefire-plugin to enable the feature:

提醒一下,要并行运行JUnit测试,我们需要配置maven-surefire-plugin以启用该功能。

<build>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.2</version>
        <configuration>
            <parallel>methods</parallel>
            <useUnlimitedThreads>true</useUnlimitedThreads>
        </configuration>
    </plugin>
</build>

You can check out the reference documentation for a more detailed configuration on parallel test execution.

你可以查看参考文献,以了解关于并行测试执行的更详细配置。

3. Concurrent Test

3.并行测试

The following example test would fail when running in parallel for versions prior to Spring 5.

对于Spring 5之前的版本,以下示例测试在并行运行时会失败。

However, it will run smoothly in Spring 5:

然而,它将在Spring 5中顺利运行。

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = Spring5JUnit4ConcurrentTest.SimpleConfiguration.class)
public class Spring5JUnit4ConcurrentTest implements ApplicationContextAware, InitializingBean {

    @Configuration
    public static class SimpleConfiguration {}

    private ApplicationContext applicationContext;

    private boolean beanInitialized = false;

    @Override
    public void afterPropertiesSet() throws Exception {
        this.beanInitialized = true;
    }

    @Override
    public void setApplicationContext(
      final ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Test
    public void whenTestStarted_thenContextSet() throws Exception {
        TimeUnit.SECONDS.sleep(2);
 
        assertNotNull(
          "The application context should have been set due to ApplicationContextAware semantics.",
          this.applicationContext);
    }

    @Test
    public void whenTestStarted_thenBeanInitialized() throws Exception {
        TimeUnit.SECONDS.sleep(2);
 
        assertTrue(
          "This test bean should have been initialized due to InitializingBean semantics.",
          this.beanInitialized);
    }
}

When running sequentially, the tests above would take around 6 seconds to pass. With concurrent execution, it will only take about 4.5 seconds – which is quite typical for how much time we can expect to save in larger suites as well.

当顺序运行时,上面的测试将需要大约6秒才能通过。在并发执行的情况下,只需要大约4.5秒–这对于我们在大型套件中预期节省的时间来说是非常典型的。

4. Under the Hood

4.引擎盖下

The primary reason prior versions of the framework didn’t support running tests concurrently was due to the management of TestContext by the TestContextManager.

先前版本的框架不支持并发运行测试的主要原因是由于TestContextTestContextManager管理。

In Spring 5, the TestContextManager uses a thread local – TestContext – to ensure that operations on TestContexts in each thread would not interfere with each other. Thus thread-safety is guaranteed for most method level and class level concurrent tests:

Spring 5中,TestContextManager使用线程本地 – TestContext – 以确保每个线程中对TestContexts的操作不会相互干扰。因此,对于大多数方法级和类级的并发测试来说,线程安全是有保障的。

public class TestContextManager {

    // ...
    private final TestContext testContext;

    private final ThreadLocal<TestContext> testContextHolder = new ThreadLocal<TestContext>() {
        protected TestContext initialValue() {
            return copyTestContext(TestContextManager.this.testContext);
        }
    };

    public final TestContext getTestContext() {
        return this.testContextHolder.get();
    }

    // ...
}

Note that the concurrency support does not apply to all kinds of tests; we need to exclude tests that:

注意,并发支持并不适用于所有种类的测试;我们需要排除那些的测试。

  • change external shared states, such as states in caches, databases, message queues, etc.
  • require specific execution orders, for example, tests that use JUnit‘s @FixMethodOrder
  • modify the ApplicationContext, which are generally marked by @DirtiesContext

5. Summary

5.总结

In this quick tutorial, we’ve shown a basic example using Spring 5 to run tests in parallel.

在这个快速教程中,我们展示了一个使用Spring 5来并行运行测试的基本例子。

As always, the example code can be found over on Github.

一如既往,示例代码可以在Github上找到over