1. 引言
從 JUnit 4 開始,測試可以並行運行以獲得更大套件的速度優勢。此前,由於 Spring TestContext Framework 不完全支持併發測試執行,因此併發測試執行存在問題。 Spring 5 之前,Spring TestContext Framework 對併發測試執行的支持不足。
在本文中,我們將演示如何使用 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 = 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 的管理由 TestContextManager 負責。
在 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();
}
// ...
}請注意,併發支持不適用於所有類型的測試;我們需要排除以下測試:
- 修改外部共享狀態,例如緩存、數據庫、消息隊列等。
- 需要特定執行順序的測試,例如使用 JUnit 的 @FixMethodOrder
- 修改 ApplicationContext,通常通過 @DirtiesContext 標記
5. 總結
在本快速教程中,我們演示了一個基本示例,使用 Spring 5 並行運行測試。