В CI UI-тест падает только при параллельном запуске: какое свойство тестовой архитектуры следует проверить первым?
Сначала следует проверить изоляцию тестов: не используют ли они общие данные, учетные записи, файлы, браузерные профили или состояние окружения. Падение только при параллельном запуске обычно означает, что один тест изменяет ресурс, от которого зависит другой.
Повторный запуск может временно скрыть симптом, но не устраняет гонку. Надежное решение — сделать тесты независимыми или явно синхронизировать доступ к неизбежно общим ресурсам.
Параллельный запуск появился как способ сократить длительность больших наборов автотестов и эффективнее использовать вычислительные ресурсы CI. Однако ускорение меняет порядок выполнения и повышает вероятность одновременного доступа к одним ресурсам.
В последовательном запуске скрытая зависимость между тестами может оставаться незаметной: предыдущий тест случайно подготавливает состояние для следующего. Параллельное выполнение делает такие зависимости видимыми, поэтому изоляция стала базовым свойством масштабируемой архитектуры автотестов.
Общие ресурсы могут быть явными: одна тестовая запись, пользователь, каталог, очередь сообщений или браузерный профиль. Они могут быть и неявными: общий кэш, фиксированный порт, глобальная настройка окружения или данные, которые тест не очищает после завершения.
Если два теста одновременно изменяют один ресурс, результат зависит от временного порядка операций. Это создает гонку, нестабильные падения и ложное ощущение надежности после повторного запуска.
Неверное решение увеличивает стоимость CI: команда тратит время на разбор случайных ошибок, а автоматический повтор может пропустить реальную регрессию. Кроме того, принудительное отключение параллельности маскирует архитектурный дефект и замедляет обратную связь.
Проверку следует начинать с поиска общих изменяемых ресурсов. Нужно сопоставить падающие тесты с их данными, учетными записями, внешними системами и временными файлами, затем проверить, могут ли они выполняться одновременно.
Основной механизм исправления — изоляция:
Для генерации данных нужны устойчивые уникальные идентификаторы, но одной случайности недостаточно: тест должен уметь найти, проверить и удалить созданные ресурсы. Полезно связывать данные с идентификатором запуска или теста, чтобы упростить диагностику и очистку.
Если общий ресурс неизбежен, применяют ограничение параллелизма, блокировку или выделяют отдельную группу тестов. Это компромисс: тесты становятся стабильнее, но часть выигрыша от параллельного запуска теряется.
Повторный запуск допустим как механизм обработки временных сбоев инфраструктуры, но не как исправление гонки. В CI также полезно сохранять логи, идентификаторы данных и порядок событий, иначе воспроизвести проблему будет трудно.
В наборе из нескольких сотен UI-тестов два сценария периодически падали только на CI-агентах. Оба использовали одну тестовую учетную запись: один сценарий менял ее настройки, а другой ожидал исходное состояние.
Рассматривались три варианта. Полностью отключить параллельность было проще всего, но это увеличивало время проверки. Добавить повторный запуск уменьшало число красных сборок, однако сохраняло гонку и могло скрыть дефект. Создавать отдельную учетную запись для каждого запуска требовало изменений в подготовке данных, зато устраняло общий изменяемый ресурс.
Выбрали третий вариант, а для ограниченного набора неизбежно общих сценариев оставили последовательный запуск. В результате тесты сохранили большую часть параллельности, а падения, зависящие от порядка выполнения, исчезли; диагностика также стала проще благодаря привязке данных к идентификатору запуска.
Нет. Уникальные записи не защищают от общих учетных данных, файлов, кэшей, очередей, портов или глобальных настроек. Изоляцию нужно оценивать по всему графу зависимостей теста, включая внешние сервисы и состояние самого тестового раннера.
Это допустимо, если ресурс невозможно безопасно разделить, а его использование локализовано и явно обозначено. Такой вариант должен быть осознанным архитектурным компромиссом, а не результатом случайного падения; иначе общие зависимости будут постепенно распространяться на новые тесты.
Нужно сравнить результаты последовательного и параллельного запуска, проверить корреляцию падений с конкретными тестами и изучить временную последовательность операций. Если ошибка зависит от соседнего теста или исчезает при изоляции данных, вероятна гонка; если она сохраняется независимо от параллельности и связана с тайм-аутами или недоступностью сервиса, следует отдельно исследовать инфраструктурную нестабильность.