Flaky-тест - тот, что то проходит, то падает без изменений в коде, - опаснее честно упавшего. Упавший указывает на проблему; флаки подрывает доверие ко всему набору: команда привыкает перезапускать до зелёного и вместе с шумом пропускает настоящие регрессии. Retry в Playwright тут - не лекарство, а датчик: он сообщает, что тест нестабилен, но не устраняет причину. Лечат не retry, а то, что за ним стоит.
Playwright сам классифицирует такие тесты. Если первая попытка упала, а retry прошёл, тест помечается как flaky - и это ценный сигнал, а не повод расслабиться. Флаки-тест нужно считать дефектом наравне с упавшим: выводить отдельной метрикой в отчёте, назначать ему владельца и срок починки. Молчаливо полагаться на то, что retry "вытянет", - значит копить нестабильность, которая однажды прорвётся в самый неудобный момент.
Почти у каждого флака - конкретная, опознаваемая причина. Неверное ожидание даёт таймаут на локаторе - тест не дождался бизнес-состояния. Общие данные роняют сценарий только при параллельном прогоне. Анимация или overlay перехватывают клик. Нестабильная внешняя сеть отвечает 429 или 5xx. Зависимость от времени или часового пояса роняет тест ночью или в конце месяца. Диагноз почти всегда сводится к одной из этих категорий.
Полезно один раз свести причины, симптомы и решения в одну таблицу, чтобы при флаке идти по диагнозу, а не гадать. Ниже - карта: по симптому находят вероятную причину и адресное решение. К ней возвращаются каждый раз, когда тест повёл себя нестабильно: сначала классифицируют симптом, потом применяют соответствующее лечение, а не вешают retry поверх непонятого падения.
| Причина | Симптом | Решение |
|---|---|---|
| Неверное ожидание | Timeout на локаторе | Ждать бизнес-состояние (web-first) |
| Общие данные | Падает только параллельно | Namespace по worker |
| Анимация / overlay | Click intercepted | Ждать actionability, чинить UI |
| Внешняя сеть | 429 / 5xx | Контролируемая граница, sandbox |
| Время / TZ | Падает ночью или в CI | page.clock, фиксированная дата |
Каждой причине соответствует своё лечение, и оно почти всегда - устранение недетерминизма, а не маскировка. Неверное ожидание чинят переходом на web-first проверку бизнес-состояния. Общие данные - namespace по worker. Перехваченный клик - ожиданием actionability или починкой overlay. Нестабильную сеть - переносом на контролируемую границу. Зависимость от времени - фиксацией часов через page.clock. Симптом указывает прямо на инструмент из предыдущих глав.
Метод борьбы с флаком тот же, что в отладке: воспроизвести, потом чинить. Недетерминизм воспроизводят детерминированно - фиксируя данные, порядок, часы, - и только поймав стабильное падение, устраняют его источник. Флак, побеждённый воспроизведением, не возвращается; флак, замазанный retry, лишь притаился. Поэтому retry держат как страховку от редкой инфраструктурной икоты, а не как способ не разбираться в причине.
У retry есть узкая законная роль. На CI пара повторов оправдана против действительно внешней, неустранимой нестабильности - мигнувшей сети между дата-центрами, - чтобы одна случайность не роняла весь пайплайн. Но это карантин, а не лечение: тест, регулярно проходящий только со второй попытки, остаётся дефектом, который стоит в очереди на починку, а не "работающим". Retry выигрывает время, но не закрывает проблему.
Типичные провалы борьбы с флаком сводятся к тому, чтобы его терпеть. Перезапускать прогон до зелёного вместо расследования причины. Вешать retry на нестабильный тест, пряча гонку данных или неверное ожидание. Оставлять зависимость от реального времени и удивляться падениям в CI с другим часовым поясом. У каждого флака есть причина из понятного списка - найдите её по симптому и устраните инструментом из книги, а не глушите повторным запуском.