Рубрики
Без рубрики

Загадка тестов на Вялую Пружинную загрузку

Большинство из нас сталкивалось с убеждениями, которые не подвергаются сомнению. Такие идеи могут варьироваться от таких мелких повседневных дел, как… Помечено как java, тестирование, spring boot, программирование.

Большинство из нас сталкивалось с убеждениями, которые не подвергаются сомнению. Такие идеи могут варьироваться от мелких повседневных дел, таких как покупка продуктов, до чего-то столь серьезного, как религиозные вопросы. В моем случае это было профессиональное убеждение, в частности, относительно того, являются ли интеграционные тесты слишком медленными или нет.

В нашей команде (и в нашем подразделении тоже) было убеждение, что интеграционные тесты проходят медленно, и это убеждение заставило нас не включать эту проблему в нашу техническую задолженность, и мы все приняли это как факт. Это продолжалось до тех пор, пока время сборки в некоторых микросервисах не превысило 10 минут, и товарищи по команде не начали жаловаться на них. Первой реакцией было по возможности избегать интеграционных тестов и вместо этого использовать модульные тесты. Я знаю, что это не мудрое решение, против которого есть много аргументов, но это было решение, которое приняла команда. В этом посте мы увидим, как была решена эта проблема, и время сборки в наших микросервисах сократилось вдвое.

Наконец, в выходные я решил заняться этим вопросом и найти ответ на вопрос: Почему наши интеграционные тесты такие медленные? Я начал с того, что записал все, к чему относился скептически, но мой разум был просто одержим @SpringBootTest .

После первого подозрения

Всякий раз, когда проводились наши тесты, я видел, как несколько раз появлялся логотип Spring. Я думал, что @@Springboost загружается полный контекст приложения для каждого тестового класса. После десятиминутных поисков я понял, что все мои предположения были неверны. Я нашел этот пункт в Spring docs :

Как только платформа TestContext загрузит ApplicationContext (или WebApplicationContext) для теста, этот контекст будет кэширован и повторно использован для всех последующих тестов, которые объявляют ту же уникальную конфигурацию контекста в том же наборе тестов.

Так почему же он все еще загружал контекст для каждого тестового класса? На самом деле этого не произошло. Я получил это, подсчитав загрузку контекста по определенному фрагменту текста в журнале (который повторяется каждый раз, когда Spring загружает контекст приложения). Что-то вроде этого:

mvn clean install > build-log.txt

grep "The following profiles are active: test" log.txt| wc -l

Результат составил 16, в то время как у нас было 46 классов интеграционных тестов в кодовой базе. Это означает, что он не загружал контекст для каждого тестового класса, но почему только 16 раз?

Копание в тестовых классах

Получив этот странный результат от подсчета контекстных нагрузок, я проверил все тестовые классы один за другим, чтобы найти подсказку. Я понял, что все классы интеграционных тестов были аннотированы @TestPropertySource для загрузки одного или нескольких определенных файлов свойств. У меня также была еще одна странная находка в этом расследовании: Макет боба и / аннотации в интеграционных тестах. У меня были не только философские проблемы с использованием этих аннотаций в интеграционных тестах, но и кричало ли это о том, что: “ Эй, чувак, здесь пахнет кодом

Я начал второй раунд поиска, и так же, как и в предыдущем, мне удалось найти тонны полезных статей и сообщений в блогах, связанных с контекстом, и некоторые подсказки примерно через десять минут. Я нашел это полезное сообщение в блоге Хосе Карлоса Валеро Санчеса об оптимизации интеграционных тестов Spring boot и это что его автор прошел тот же путь, что и я.

Прочитав эти и те же статьи, я понял, что в интеграционных тестах есть некоторые подводные камни, которые не позволяют Spring повторно использовать загруженный контекст приложения в интеграционных тестах. Вот наиболее важные из них:

  • Использование @Mockbeat и @@Соя
  • Использование @DirtiesContext
  • Неосторожное использование профилей в интеграционных тестах
  • Неосторожное использование @TestPropertySource

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

Оптимизация интеграционных тестов

Как я уже упоминал ранее, в наших классах интеграционных тестов было два подводных камня. Первым из них было аннотирование всех интеграционных тестов с помощью @TestPropertySource . Я быстро взглянул на них и обнаружил, что для каждого класса используются разные комбинации файлов свойств. Например:

@TestPropertySource({
    "classpath:x.properties",
    "classpath:y.properties"
})
public class TestOne{
    //...
}

@TestPropertySource({
    "classpath:z.properties",
    "classpath:y.properties"
})
public class TestTwo{
    //...
}

@TestPropertySource({
    "classpath:x.properties",
    "classpath:z.properties"
})
public class TestThree{
    //...
}

@TestPropertySource({
    "classpath:x.properties",
    "classpath:z.properties"
})
public class TestFour{
    //...
}

В этой ситуации Spring загружает контекст для каждой уникальной комбинации файлов свойств. Например, в предыдущем примере он загружает контекст три раза, потому что x и z повторялись два раза, поэтому Spring может повторно использовать контекст для них обоих.

Поэтому я решил исключить аннотацию TestPropertySource из всех классов интеграционных тестов и объединить их в абстрактный класс, который все они расширяют. Вот новый стиль тестовых классов:

@TestPropertySource({
    "classpath:x.properties",
    "classpath:y.properties",
    "classpath:z.properties"
})
public abstract class AbstractTes{
    //...
}

public class TestX extends AbstractTest{
    //...
}

Теперь, в этом примере, он загружает контекст только один раз. Я сделал что-то подобное в кодовой базе и снова подсчитал загрузку контекста. Результат был многообещающим: количество контекстных загрузок сократилось до 2 (с 16). Обратите внимание, что, например, если у вас есть свойство с именем database.url оба в x.properties и z.свойства , второй переопределяет первый.

Я прошел оставшиеся тестовые занятия. Знаешь что? Все они содержали либо @Mockbeat или @Соя . Как я уже упоминал ранее, в отдельном сообщении в блоге я расскажу об использовании этих причудливых аннотаций и проблемах, которые они могут вызвать в интеграционных тестах.

Извлеченный урок

Весь процесс занял от 3 до 4 часов. Следовательно, время сборки на Дженкинсе сократилось с 10 до 4 минут. Это означает, что иногда мы могли бы сэкономить много времени, потратив несколько часов на решение таких проблем, которые в долгосрочной перспективе отнимают значительное количество времени.

В ближайшее время я рассмотрю проблемы производительности в интеграционных тестах в отдельном сообщении в блоге. Я был бы признателен, если бы вы поделились своими комментариями.

Оригинал: “https://dev.to/dante0747/sluggish-spring-boot-tests-riddle-4ej6”