Spring 5中的并发测试执行

2023/05/10

1. 概述

从JUnit 4开始,测试可以并行运行以加快大型测试套件的速度。问题在于Spring 5之前的Spring TestContext框架不完全支持并发测试执行。

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

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 = Spring5JUnit4ConcurrentIntegrationTest.SimpleConfiguration.class)
public class Spring5JUnit4ConcurrentIntegrationTest implements ApplicationContextAware, InitializingBean {

    private ApplicationContext applicationContext;
    private boolean beanInitialized = false;

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

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

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

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

    @Configuration
    public static class SimpleConfiguration {
    }
}

按顺序运行时,上述测试大约需要6秒才能通过。通过并发执行,它只需要大约4.5秒-这对于我们在更大的套件中可以节省的时间来说是非常典型的。

4. 原理

框架的早期版本不支持并行运行测试的主要原因是由于TestContextManager对TestContext的管理。

在Spring 5中,TestContextManager使用ThreadLocal TestContext来确保每个线程中对TestContext的操作不会相互干扰。因此,大多数方法级别和类级别的并发测试都保证了线程安全:

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上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章