Надёжность автотестов
Посчитаем, что значит «почти стабильный набор». Пусть каждый тест мигает всего в 0,5% прогонов. Тогда набор из 50 тестов будет полностью зелёным в 78% случаев, из 200 - уже только в 37%, а из 1000 - в 0,7%. То есть при тысяче тестов и полупроценте нестабильности зелёный прогон вы увидите примерно раз в сто пятьдесят запусков.
Стержень: нестабильные тесты убивают доверие ко всему набору, поэтому их чинят или уводят из блокирующего прогона, но не перезапускают до зелёного.
// Формулировки: «что такое флаки-тест?», «откуда берётся нестабильность?», «что делать с мигающим тестом?».
Арифметика нестабильности
Флаки-тест (от английского flaky, «хлопьями осыпающийся») - это тест, который на одном и том же коде то падает, то проходит. Считают его частоту: сколько падений на сто прогонов при неизменном коде.
Почему даже крошечная частота смертельна, показывает счёт. Чтобы прогон был зелёным, должны пройти все тесты сразу, поэтому вероятности перемножаются. При нестабильности 0,5% на тест: 50 тестов - 78% зелёных прогонов, 200 - 37%, 1000 - 0,7%. Уменьшим нестабильность впятеро, до 0,1%: 1000 тестов дадут зелёный прогон в 37% случаев. Всё равно каждый второй прогон красный - и это при почти идеальных тестах.
// Отсюда главный вред, и он не в потерянном времени. Команда привыкает: красное - это «наверное, опять мигает». Перезапускают не глядя. И однажды так перезапустят настоящий дефект, который уедет в прод, то есть к живым пользователям. Набор, которому не верят, хуже отсутствия набора: он ещё и создаёт ложное чувство защищённости.
- флаки-тест
- на неизменном коде результат меняется от прогона к прогону
- почему перемножается
- прогон зелёный, только если прошли все тесты сразу
Три источника и лечение каждого
Первый - ожидание времени вместо события. Замер: тест ждёт фиксированные 15 миллисекунд там, где ответ приходит за 5-25, - падает в 51% прогонов. Поставил паузу 30 миллисекунд - падений ноль, но каждый тест теперь стоит вдвое дороже: на двухстах тестах 6 секунд вместо 3. А ожидание самого условия дало ноль падений при той же скорости, что у короткой паузы. Лечение: ждать событие, а не отрезок времени.
Второй - общие данные. Я запустил 60 тестов, работающих с одним списком, в несколько потоков. На одном потоке прошли все. На четырёх провалились 59 из 60: каждый тест ожидал увидеть результат только своих действий, а видел ещё и чужие. Лечение: свои данные на каждый тест, уникальные имена, очистка после себя. Параллельный запуск тут работает как проявитель - если тесты падают только вместе, они делят состояние.
// Третий - зависимость от времени и случайности. Я проверил наивную функцию «прибавить месяц к сегодняшней дате»: в 2026 году она падает ровно в 7 дней - 29, 30 и 31 января, 31 марта, мая, августа и октября. То есть тест зелёный 358 дней в году, а в семь дней краснеет, и разработчик честно отвечает «не воспроизводится». Лечение: фиксированная дата в тесте вместо сегодняшней и заданное зерно случайности - число, с которого генератор начинает, чтобы последовательность повторялась от прогона к прогону.
- изоляция теста
- свои данные, независимость от порядка и от соседей
- детерминизм
- одинаковый вход даёт одинаковый результат: фикс времени и зерно случайности
Перезапуск, карантин, наблюдение
Самое популярное «лечение» - перезапуск упавшего теста - на самом деле обезболивающее. Посчитаем: тест мигает в 20% прогонов, ставим три повтора. Вероятность, что хотя бы один из них зелёный, - 99,2%. Прогон становится зелёным почти всегда, и проблема исчезает с радаров. Ровно с той же вероятностью прячется и настоящий редкий дефект: он ведь тоже проявляется не каждый раз.
Что делать вместо. Если тест чинится сейчас - чиним. Если нет - уводим его из блокирующего прогона в карантин, но обязательно с заведённой задачей и сроком. Карантин без задач - тихое кладбище, куда тесты падают и больше не возвращаются, а дыры в проверках копятся молча.
// И за стабильностью наблюдают числами: доля успешных прогонов по каждому тесту за месяц, список самых мигающих, время прогона. Тогда чинят по данным, а не по последнему впечатлению. Отдельно фиксируют окружение: версии браузера и библиотек, подменённые внешние сервисы - всё, что делает прогон повторяемым.
- карантин
- мигающий тест вынесен из блокирующего прогона и снабжён задачей на починку
- цена перезапуска
- три повтора прячут мигание в 99% случаев - вместе с редким настоящим багом
Как отвечать: «Что такое флаки-тест и что с ним делать?»
Флаки-тест - это тест, который на одном и том же коде то падает, то проходит. Опасен он не сам по себе, а масштабом: чтобы прогон был зелёным, должны пройти все тесты, поэтому вероятности перемножаются. Я считал: при нестабильности всего полпроцента на тест набор из двухсот тестов зелёный лишь в тридцати семи процентах прогонов, а из тысячи - меньше чем в одном. Дальше включается человеческий фактор: команда привыкает, что красное - это «опять мигает», перезапускает не глядя, и однажды так пропустит настоящий баг. Поэтому мигание я не игнорирую, а ищу причину, и причин по сути три. Ожидание времени вместо события - замерял, фиксированная пауза давала пятьдесят один процент падений, ожидание условия ноль при той же скорости. Общие данные - шестьдесят тестов на одном наборе данных в четыре потока провалили пятьдесят девять из шестидесяти, лечится изоляцией. И зависимость от даты и случайности - лечится фиксированной датой и зерном. Если починить прямо сейчас нельзя, увожу тест в карантин с задачей. Чего не делаю - не перезапускаю до зелёного: три повтора прячут мигание в девяноста девяти процентах случаев, а вместе с ним и редкий настоящий дефект.
Почему это сильный ответ: вред показан счётом по всему набору, названы три источника с измеренным лечением каждого, и явно отвергнут перезапуск с объяснением, что именно он маскирует.
На чём валят
- −Перезапускать до зелёного: три повтора прячут мигание в 99% случаев вместе с редким багом.
- −Лечить мигание увеличением паузы - вдвое медленнее, а на другой машине опять упадёт.
- −Считать 0,5% нестабильности мелочью: на 200 тестах зелёный прогон остаётся в 37% случаев.
- −Общие тестовые данные: те же 60 тестов в 4 потока провалили 59.
- −Сегодняшняя дата в тесте - 7 дней в году красных и вечное «не воспроизводится».
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 12, остальные разбираются в тренажёре.
- Каковы частые причины флаки-тестов?A)Основная причина флаки — слишком короткий таймаут, и лечится он его увеличениемB)Гонки и асинхронность, зависимость от порядка и общих данных, время и рандом, обращения к внешним сервисамC)Флаки возникает от опечаток в коде теста, которые компилятор не смог обнаружить заранееD)Тесты флакают из-за нехватки оперативной памяти на машине непрерывной интеграции
показать ответ и разбор
+B)Гонки и асинхронность, зависимость от порядка и общих данных, время и рандом, обращения к внешним сервисам// разбор: Флаки рождается из любого источника недетерминизма. Асинхронность и гонки: тест обгоняет страницу или два действия переплетаются непредсказуемо. Зависимость от порядка и общих данных: тест опирается на состояние, оставленное другим, и ломается при перестановке или параллели. Время и рандом: привязка к текущей дате, таймзоне, случайным значениям. Внешние сервисы: недоступность, задержки, меняющиеся ответы стороннего API. Диагностика флаки сводится к поиску, какой из этих факторов вносит непостоянство, и его устранению — ожиданиями, изоляцией данных, фиксированным временем/сидом, моками внешних сервисов.
- Почему автотесты стараются делать независимыми друг от друга?A)Чтобы тесты выполнялись строго в одном и том же порядке при каждом прогоне набораB)Чтобы уменьшить общее число тестов за счёт объединения зависимых проверок в одинC)Каждый стартует из известного состояния и не зависит от другихD)Чтобы каждый тест мог напрямую менять данные, подготовленные предыдущим тестом
показать ответ и разбор
+C)Каждый стартует из известного состояния и не зависит от других// разбор: Независимый тест сам готовит нужное состояние (данные, сессию) и не опирается на то, что оставил предыдущий. Это даёт три важных свойства. Первое — любой порядок: тесты можно переставлять и запускать по одному, не таща за собой цепочку предшественников. Второе — параллельный прогон: независимые тесты не мешают друг другу и ускоряют набор. Третье — точная диагностика: падение указывает именно на свой тест, а не на «сломалось где-то раньше по цепочке». Зависимые же тесты рушатся каскадом, флакают от перестановки и скрывают, что реально сломано. Независимость обеспечивают через setup/teardown и изоляцию данных.
- Что делают setup и teardown в автотесте?A)Setup запускает тест, а teardown формирует итоговый отчёт о его прохожденииB)Setup компилирует код теста, а teardown выгружает его из памяти после прогонаC)Setup и teardown замеряют время выполнения теста для отчёта о производительностиD)Setup готовит данные и состояние до теста, teardown убирает следы после
показать ответ и разбор
+D)Setup готовит данные и состояние до теста, teardown убирает следы после// разбор: Setup (подготовка, fixture) выполняется до теста и приводит систему в известное исходное состояние: создаёт нужные данные, логинит пользователя, поднимает соединения. Teardown выполняется после теста и убирает за ним следы: удаляет созданные записи, закрывает соединения, откатывает изменения. Вместе они обеспечивают, что каждый тест стартует «с чистого листа» и не оставляет мусора для следующих. Это фундамент независимости и воспроизводимости: без setup тест зависит от случайного текущего состояния, без teardown — засоряет среду и роняет соседей. Многие фреймворки дают хуки before/after для этого.
- Почему нельзя завязывать один тест на результат другого (на порядок)?A)Порядок-зависимый набор рушится при перестановке или параллели и прячет, какой тест на самом деле сломанB)Потому что зависимые тесты выполняются медленнее независимых на той же машине CIC)Потому что фреймворки автоматизации запрещают запускать тесты в заданном порядкеD)Потому что тест, зависящий от другого, не попадает в итоговый отчёт о прогоне
показать ответ и разбор
+A)Порядок-зависимый набор рушится при перестановке или параллели и прячет, какой тест на самом деле сломан// разбор: Если тест B рассчитывает, что тест A уже создал запись и залогинил пользователя, набор становится хрупкой цепочкой. Переставили тесты, отключили один, запустили в параллель или по одному — и B падает не потому, что в нём баг, а потому что A не отработал первым. Такое падение вводит в заблуждение: красный B, а причина в A или в порядке. Кроме того, зависимость запрещает параллельный прогон (ускорение) и осложняет отладку — непонятно, что чинить. Правильно — каждый тест самодостаточен: сам создаёт нужное состояние в setup, не полагаясь на соседей. Тогда падение точно указывает на виновника.
- Авто-ретрай упавшего флаки-теста — это решение проблемы или её маскировка?A)Ретрай устраняет флаки, поскольку при повторе тест уже не сталкивается с гонкойB)Ретрай прячет симптом, но не устраняет причинуC)Ретрай ускоряет набор, потому что перезапуск теста выполняется быстрее первого прогонаD)Ретрай нужен, чтобы отличить флаки от реального бага: если прошёл со второго раза — это баг
показать ответ и разбор
+B)Ретрай прячет симптом, но не устраняет причину// разбор: Автоматический перезапуск упавшего теста (retry until pass) делает сборку зелёной, скрывая факт нестабильности. Проблема в том, что это лечение симптома: причина недетерминизма (гонка, данные, время) остаётся, тест продолжает флакать, а команда теряет сигнал о реальной нестабильности. Хуже — ретрай может замаскировать и настоящий периодический баг продукта, который проявляется через тот же нестабильный тест. Разумнее: разобраться в причине флаки и устранить её (ожидания, изоляция, моки), а пока не починили — временно вынести тест в карантин (не блокирует сборку, но виден). Ретрай допустим как крайняя мера с обязательным трекингом, а не как способ «замести» флаки.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.