1. Введение
Начиная с JUnit 4
, тесты можно запускать параллельно, чтобы увеличить скорость для больших наборов. Проблема заключалась в том, что параллельное выполнение тестов не полностью поддерживалось Spring TestContext Framework
до Spring 5
.
В этой быстрой статье мы покажем, как использовать Spring 5
для одновременного запуска наших тестов в проектах Spring
.
2. Настройка Мавена
Напоминаем, что для параллельного запуска тестов 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
для параллельного запуска тестов.
Как всегда, пример кода можно найти на Github .