Обновить

При написании интеграционных тестов для Spring Boot приложения часто возникает проблема, что разработчики бездумно добавляют аннотации @MockBean, @SpyBean, @DirtiesContext или переопределяют прямо в тестовом классе различные property. Всё это приводит к изменению Spring Context, невозможности использовать закэшированный контекст и следовательно созданию нового. Часто создание нового контекста это длительная операция.

Существуют инструменты по отслеживанию этих процессов. Самым простым способом увидеть количество контекстов и количество попаданий в кэш является добавление логирования либо через свойство logging.level.org.springframework.test.context.cache=DEBUG либо настройкой вашего логгера.

Один известный автор статей про тестирование на Java / Spring Boot, Philip Riecks (со товарищи), создал инструмент с открытым исходным кодом Spring Test Profiler при помощи которого можно получить html отчёт о поднимаемых контекстах во время тестов, о количестве и типе бинов в этих контекстах. На Хабре есть перевод его статьи в сообществе Spring АйО.

У нас на проекте стал вопрос, как нам показать разработчикам, что их тест порождает новый Спринг Контекст. Мы решили считать контексты в тестах и при превышении ожидаемого количества падать. Это "руинит" сборку и CI/CD пайплайн.
Для этого мы добавили реализацию интерфейса ContextCustomizer

class LimitingSpringContextCustomizer implements ContextCustomizer {

    private static final AtomicInteger CONTEXT_COUNTER = new AtomicInteger();
    private static final int EXPECTED_SPRING_TEST_CONTEXT_COUNT = 16;

    @Override
    public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
        int numberOfContexts = CONTEXT_COUNTER.incrementAndGet();
        log.info("Number of Spring Test Contexts: {}/{}", numberOfContexts, EXPECTED_SPRING_TEST_CONTEXT_COUNT);
        Assert.state(numberOfContexts <= EXPECTED_SPRING_TEST_CONTEXT_COUNT,
                () -> "Number of test contexts exceeds configured maximum: " + EXPECTED_SPRING_TEST_CONTEXT_COUNT);
    }

    @Override
    public boolean equals(Object obj) {
        return (obj != null) && (getClass() == obj.getClass());
    }

    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
}

Здесь, согласно документации интерфейса ContextCustomizer необходимо корректно переопределить методы equals и hashCode.

WARNING: implementations must implement correct equals and hashCode methods since customizers form part of the MergedContextConfiguration which is used as a cache key.

Далее добавляем фабрику.

class LimitingSpringContextCustomizerFactory implements ContextCustomizerFactory {

    @Override
    public ContextCustomizer createContextCustomizer(Class<?> testClass,
                                                     List<ContextConfigurationAttributes> configAttributes) {
        return new LimitingSpringContextCustomizer();
    }
}

Затем регистрируем эту фабрику при помощи spring.factories чтобы она применялась ко всем тестам.
Для этого создаём в тестовых ресурсах файл по пути src/test/resources/META-INF/spring.factories со следующим содержимым

org.springframework.test.context.ContextCustomizerFactory=\  
com.mycompany.app.support.spring.LimitingSpringContextCustomizerFactory

Теперь, если во время выполнения тестов будет превышено количество инициализированных тестовых контекстов, то мы увидим ошибку в тестах и сборка завершится неудачей.

Возможно, это пример поможет кому-нибудь повысить скорость прохождения своих тестов путём отслеживания количества запускаемых тестовых Спринг контекстов.

Теги:
+4
Комментарии0

Публикации

Ближайшие события