Пара тестов на Python (pytest): def test_sort(): assert sort(array) == expected — где тест иногда падает в CI, но локально проходит; опишите алгоритм поиска флопи/флейки, типичные причины нестабильности тестов (параллелизм, неинициализированные данные, временные зависимости, глобальные состояния), стратегии стабилизации тестов и практики CI для предотвращения регрессий

24 Окт в 14:29
3 +1
0
Ответы
1
Кратко: flaky — тест, который иногда падает в CI, но локально проходит. Ниже — алгоритм поиска флейки, типичные причины, стратегии стабилизации и практики CI.
Алгоритм поиска флейки
- Воспроизведение в CI‑похожей среде:
- Запустите тест в контейнере/VM с теми же образами и зависимостями, что в CI.
- Снимите окружение (версия Python, pip freeze, переменные окружения).
- Повторные прогоны локально/в CI:
- Запустите тест многократно, чтобы измерить вероятность ошибки, напр. повторить N=100N=100N=100 раз (pytest-repeat плагин: `pytest --count=100`).
- Запустите весь набор тестов многократно (иногда порядок запуска выявляет проблему).
- Локализация: запуск только этого теста и ближайших зависимостей; отключение параллелизма (не использовать xdist) — проверить, воспроизводится ли при последовательном исполнении.
- Включить подробный вывод и трассировку (pytest `-s`, `--showlocals`, faulthandler) и логирование.
- Изолировать причину: поменять порядок тестов, выключить/включить внешние сервисы, фиксировать/менять PYTHONHASHSEED.
- Бисект коммитов при подозрении на регресс: `git bisect run `, но при флейках делать многократные прогоны на каждом шаге.
- Собирать артефакты при падении (лог, дампы, входные данные) для последующего анализа.
Типичные причины нестабильности тестов
- Параллелизм и гонки (тесты делят глобальные ресурсы, файлы, сетевые порты).
- Неинициализированные/неочищенные данные между тестами (глобальные переменные, кеши, tmp‑файлы).
- Временные зависимости (sleep‑тайминги, ожидание асинхронных событий без проверок).
- Зависимость от случайности без фиксации seed (random, numpy, uuid, PYTHONHASHSEED).
- Внешние сервисы/сеть (латентность, таймауты, ограничение запросов).
- Платформенные/системные различия (файловая система, права, локаль, версия Python).
- Нетерминированные структуры/сравнения (сравнение set/dict без сортировки, неустойчивые критерии сортировки).
- Ресурсная конкуренция в CI (CPU, память) — тесты зависят от производительности.
- Тайминги/условные проверки, завязанные на wall‑time (использование sleep вместо ожиданий на событие).
Стратегии стабилизации тестов
- Сделать тест детерминированным:
- Фиксировать генераторы: `random.seed(...)`, `numpy.random.seed(...)`, `PYTHONHASHSEED=000` или фиксированное число.
- Использовать фикстуры с ограниченной областью видимости (pytest fixtures scope="function") и гарантированные teardown.
- Изолировать среду:
- tmp_path / tmpdir для файлов; уникальные порты; мок внешних сервисов (responses, requests-mock, vcrpy).
- Запускать тесты в чистом виртуальном окружении/контейнере.
- Избегать хрупких ожиданий и sleep:
- Использовать ожидание по условию с таймаутом (polling + assert) вместо sleep.
- Управление параллелизмом:
- Если тесты не потокобезопасны, запускать их последовательно либо обеспечить синхронизацию/ломки ресурсов.
- Правильные асссерты:
- Если порядок не важен — сравнивать множества/сортированные списки или Counter.
- Если важен — явно задать ключ сортировки, чтобы результаты были однозначны.
- Мока и интеграционные заглушки:
- Для тестов, зависящих от сети/БД — мокать ответы или использовать локальные тестовые инстансы.
- Инструменты обнаружения гонок/памяти:
- Для расширений/нативного кода — запуск под sanitizer/valgrind, использование faulthandler.
- Логи и сбор контекста:
- При падении автоматически сохранять stdout/stderr, стек, окружение, seed.
Практики CI для предотвращения регрессий
- Повторные прогоны и мониторинг:
- Регулярно запускать набор тестов N раз для поиска флейков; вести метрики flakiness.
- Карантин и трекинг:
- Мечтать/маркировать временно flaky‑тесты (метка @flaky или xfail) с обязательной задачей на фиксацию.
- Релпроизуемость среды:
- Пинить зависимости, использовать контейнеры/immutable images, фиксированные образы CI.
- Принудительная последовательность:
- Запускать критические тесты в последовательном режиме как контроль.
- Сбор артефактов при падениях:
- Автоматическое сохранение логов, дампов, окружения, seed; возможность перезапуска с тем же seed.
- Автоматические ретраи с ограничением:
- Короткие ретраи (например, 1‑2) для transient ошибок, но самих flakes не скрывать навсегда.
- Тестовая параллелизация под контролем:
- Выделять отдельные worker‑контейнеры для тестов, чтобы снизить влияние соседних jobs.
- QA‑пайплайн для flaky:
- Отчеты о самых частых флейках, владельцы тестов/команд, сроки исправления.
- Регресс‑тесты и статический анализ:
- CI запускает дополнительные проверки после подозрительных коммитов (например, изменения в исходниках, затрагивающих глобальные состояния).
- Проверка конфигурации окружения:
- Явно выставлять переменные (локаль, таймзона, PYTHONHASHSEED) в CI‑скриптах.
Специфично для вашего примера (тест сортировки)
- Если порядок результата не важен: заменить `assert sort(array) == expected` на `assert sorted(sort(array)) == sorted(expected)` или `assert collections.Counter(sort(array)) == collections.Counter(expected)`.
- Если порядок важен: убедиться, что сортировка детерминирована — передавать стабильный ключ, сравнивать по ключу: `sorted(array, key=lambda x: (x.attr1, x.attr2))`.
- Проверить, не использует ли функция случайность или неявные структуры (set/dict) внутри.
- Зафиксировать seed и PYTHONHASHSEED=000 при отладке флейки.
- Отключить параллелизм и запустить многократно, чтобы убедиться, что проблема именно в нестабильности, а не в окружении.
Короткий чеклист для отладки флейки
- Воспроизвести в CI‑образе → повторить NNN раз → отключить параллелизм → зафиксировать seed/PYTHONHASHSEED → включить подробные логи → изолировать/мокнуть внешние ресурсы → если нужно — git bisect с многократной проверкой.
Если нужно — могу предложить конкретную последовательность команд для pytest и docker‑образа CI.
24 Окт в 15:04
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир