Как улучшить прогоны автотестов при помощи карантина |
08.09.2025 00:00 |
Привет, меня зовут Андрей и я автоматизатор (остальные в кругу хлопают в знак сочувствия) в hh.ru. В статье расскажу, как мы ввели карантин автотестов, повысив стабильность релизов и скорость доставки. ПредпосылкиУ нас много тестов: стандартный прогон (как и самые крупные релизы, например, монолитов) включает 2300 тестовых классов, которые содержат несколько тестовых методов, не считая датапровайдеров (мы пользуемся TestNG, в JUnit это Ретрай в нашем случае — это автоматический перезапуск упавших тестовых методов класса во время текущего прогона автотестов. Тест, который упал, но был отправлен на перезапуск, сохраняется в статистике запусков с результатом retry. Некоторые из этих тестов не проходят с первого раза довольно часто, т.е. их падения связаны с нестабильностью, а не с выпускаемыми изменениями. Падения тестов мы не любим — во время релиза это означает остановку автоматики (наши внутренние системы CI/CD) и ожидание прихода человеков, которые какое-то время разбираются в причинах падения: зовут коллег, которые скажут, что «тест просто нестабильный, перезапусти его несколько раз», или дадут добро на скип — пропуск упавших тестов, когда автор релиза самостоятельно двигает задачу в Jira в следующий статус. Релизов у нас — несколько десятков в день, так что остановка тормозит не только текущий релиз, но и те, что в очереди за ним. Такое мы любим еще меньше. РешениеМы решили попробовать несколько видов карантина для самых нестабильных тестов. Здесь и далее под тестом мы будем подразумевать тестовый класс, чтобы сэкономить буквы и когнитивную нагрузку. Карантин в статье — это список тестов, которые наш тестовый фреймворк (TestNG + собственный раннер) не будет запускать, даже если они подпадают под условия для запуска. Технически хранилище списка тестов в карантине устроено несложно: грубо говоря, это таблица в БД + HTTP API над ней, куда ходит наш раннер за списком тестов. В таблице есть такие поля, как: имя тестового класса, дата добавления в карантин, дата окончания карантина, имя добавившего тест в карантин и т. д. Самое интересное — это то, как тесты попадают в карантин. Мы придумали три варианта: 1. Ручной карантинСамый понятный вид карантина: если видим, что тест больше не проходит (обычно — в 100% случаев), то мы вручную добавляем его в карантин, чтобы релизы больше не останавливались в ожидании ручного скипа тестов. Отключать тесты можно было и раньше — через правки в коде. Но это означало создание PR (pull request), ожидание аппрува PR, мерж — и всё то же самое некоторое время спустя для включения теста обратно. Также при добавлении теста мы даём выбрать срок карантина:
Технически за это отвечает поле expiration_date в БД: по нему мы фильтруем результаты внутри API, отвечая на запросы списка текущих тестов в карантине. 2. Полуавтоматический карантинЕсли в результате повторного прогона упавших автотестов на релизе упало менее N* тестов, их можно вручную отправить на контрольный перепрогон на *N и K мы выбирали эмпирически так:
НюансыИногда тесты падают, но в карантин их отправлять нельзя — например, если уже есть рецепт для исправления ситуации и мы не хотим пропускать критические тесты. Поэтому: если сегодня тест уже попадал в полуавтоматический карантин, но был удален из него — API такой тест в карантин сегодня больше не добавит. 3. Автоматический карантин на основе статистики автотестовЭто самый интересный и сложный из наших типов карантина. Мы разработали ряд критериев, по которым считаем, что тесту пора Все Например, если медианная стабильность теста за 7 дней без учёта ретраев меньше 70%, мы заводим задачу со средним приоритетом на стабилизацию и добавляем тест в карантин. Для нас важнее количество падений, чем количество ретраев — любое падение приводит к ожиданию (автоматикой) ручного разбора тестового прогона на релизе. Есть и более мягкие критерии, по которым мы заводим задачи на стабилизацию теста без его добавления в карантин. Например, если медианная стабильность за 7 дней без учета ретраев находится в диапазоне 85-95%. Но такие задачи выглядят как техдолг, плавают как техдолг и крякают как техдолг — а его, как известно, можно копить годами (утрированно). Поэтому мы разработали мотивационные механики:
Нюансы
Удаление из карантинаМы решили не делать автоматическое удаление из карантина. Иначе может возникнуть желание «переждать бурю», ничего не делая с тестом — вдруг само рассосётся. Даже если тест нестабилен сам по себе, а не из-за каких-то массовых проблем со стендами. Зато в планах — регулярная проверка карантина для следующих случаев:
Как понять, что тест стабилизированВ нашем тестовом фреймворке есть специальный режим для проверки стабильности: запускаются все тесты, а параллельно в цикле запускается проверяемый тест. В конце смотрим, сколько раз он успешно прошёл и сколько упал. Тест считается стабилизированным, если:
А вот теперь слайды ©Скорость починки тестовАвтоматика по заведению задач у нас была и раньше — тогда она просто создавалась для тестов с совсем плачевной статистикой. Но коллеги не всегда оперативно брались за такие задачи — самым старым из них (задачам) могло быть несколько месяцев. С введением SLA и автодобавлением в карантин при просрочке ситуация заметно улучшилась: число открываемых и закрываемых задач примерно одинаковое (с поправкой на майские праздники): Соотношение тестовых прогонов без ошибок к общему числу прогоновИтогиБлагодаря автоматическому карантину и заведению задач на починку тестов с контролем SLA мы ускорили работы по стабилизации тестов и увеличили среднее количество прогонов, проходящих с первого раза — а значит, ускорили доставку наших изменений пользователям. Если у вас есть нестабильные тесты и вы хотите себе такое же, то следует:
Вот и всё. |