1. 引言

JUnit 4 起,我们可以在大型测试套件中并行运行测试以提高速度。但在 Spring 5 之前,Spring TestContext Framework 对并发测试执行的支持并不完整。

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

2. Maven 配置

为了在并行模式下运行 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>

关于并行测试执行的更详细配置,请参考官方文档

3. 并发测试

以下示例测试在 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);
    }
}

顺序执行时,上述测试大约需要6秒通过。在并发执行下,只需约4.5秒——对于大型测试套件,这是我们可以期望节省的时间典型水平。

4. 深入理解

TestContext Manager 是以前框架版本不支持并发测试的主要原因,因为它负责管理 TestContext

Spring 5 中,TestContext Manager 使用线程局部变量— 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();
    }

    // ...
}

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

  • 修改外部共享状态的测试,如缓存、数据库、消息队列等的状态。
  • 需要特定执行顺序的测试,例如使用 JUnit@FixMethodOrder
  • 修改 ApplicationContext 的测试,通常标记为@DirtiesContext

5. 总结

在这篇简短教程中,我们展示了使用 Spring 5 进行并行测试的基本示例。

如往常一样,示例代码可以在GitHub上找到。


« 上一篇: Axon框架指南