Урок по треку Автоматизация тестирования 8 мин

Ожидания и флаки за 8 минут

Явные и неявные ожидания, поллинг, ретраи и борьба с нестабильными тестами.

Флаки (flaky test) это тест, который на одном и том же коде то проходит, то падает. Один флаки в наборе из 500 тестов, и через месяц команда перестаёт смотреть на красный билд. Поэтому вопросы про ожидания и нестабильность есть почти на каждом собесе автоматизатора.

Почему тест не может просто кликнуть

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

sleep: почему нельзя

time.sleep(5)
driver.find_element(By.ID, "pay").click()

Если страница грузится 2 секунды, тест 3 секунды простаивает. Если 6 (нагруженный CI), тест падает. Сотня таких пауз, и регресс идёт лишние полчаса, а флаки всё равно остаются.

Ожидания в Selenium

  • Неявное (driver.implicitly_wait(10)): глобальная настройка. Каждый find_element до 10 секунд опрашивает DOM, пока элемент не появится. Ждёт только наличия, а не видимости или кликабельности.
  • Явное (WebDriverWait(driver, 10).until(EC.element_to_be_clickable(...))): ждёт конкретного условия, опрашивая его. В Python интервал опроса по умолчанию 0.5 секунды.
  • Fluent wait (в Java FluentWait): явное ожидание с настройкой интервала и списка игнорируемых исключений.

Документация Selenium прямо предупреждает: не смешивайте неявные и явные ожидания, время ожидания становится непредсказуемым.

Ожидания в Playwright

Playwright ждёт сам. Перед кликом он проверяет, что элемент прикреплён к DOM, видим, стабилен (не анимируется), получает события и включён. Проверки await expect(locator).toHaveText(...) повторяются, пока не станут истинными или не выйдет таймаут (по умолчанию 5 секунд). Поэтому в Playwright ручные ожидания нужны редко, а page.waitForTimeout() в тестах считается запахом.

Поллинг своими руками

Иногда ждать надо не элемент, а внешний мир: письмо в почтовом ящике, статус заказа в API, запись в базе.

def wait_until(check, timeout=10, interval=0.5):
    deadline = time.monotonic() + timeout
    while True:
        if check():
            return
        if time.monotonic() >= deadline:
            raise TimeoutError("условие не выполнилось")
        time.sleep(interval)

Здесь sleep уместен: это интервал опроса, а не надежда «вдруг успеет». time.monotonic() не прыгает при переводе системных часов.

Ретраи и экспоненциальная задержка

Для сетевых вызовов в тестах (подготовка данных через API) ретрай оправдан на временные ошибки: 429, 502, 503, 504, обрыв соединения. На 400 или 404 повтор ничего не даст. Задержка растёт: 0.5, 1, 2, 4 секунды, с потолком.

Откуда берутся флаки

  1. Гонки: тест не дождался нужного состояния.
  2. Зависимость от порядка и общих данных: тесты делят пользователя или корзину.
  3. Время: часовые пояса, «сегодня» около полуночи, sleep.
  4. Внешние сервисы и нестабильный стенд.
  5. Случайные данные без фиксированного seed.
  6. Утечки состояния: кука, localStorage, незакрытая транзакция.

Как с ними жить

  • Ретраи теста (retries в Playwright, pytest-rerunfailures) снимают симптом. Пусть флаки помечаются (в Playwright такой тест получает статус flaky), но обязательно разбираются.
  • Карантин: известные флаки уходят в отдельный набор, не блокируют мерж, но имеют владельца и срок.
  • Метрика: доля флаки по истории прогонов. Тест, который на одном коммите и падал, и проходил, флаки по определению.
  • Воспроизведение: прогнать один тест 50 раз подряд (--repeat-each в Playwright, pytest-repeat), посмотреть трейс и видео.

Что спросят на собесе

  • Чем неявное ожидание отличается от явного и почему их не смешивают?
  • Почему sleep плох и когда он допустим?
  • Как Playwright понимает, что можно кликать?
  • Назовите причины флаки и как вы их ищете.
  • Нормально ли лечить флаки ретраями?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.