Перейти к основному содержимому

Параллельное выполнение тестов в Spring 5

· 3 мин. чтения

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 .