Глубокое погружение в pytest-testmon: Умный отбор тестов для Python-проектов
Глубокое погружение в pytest-testmon: Умный отбор тестов для Python-проектов
Что такое pytest-testmon и зачем он нужен?
pytest-testmon — это интеллектуальный плагин для pytest, который автоматически определяет, какие тесты нужно запустить после изменений кода. Вместо полного прогона всех тестов каждый раз (что может занимать часы в крупных проектах), testmon использует анализ зависимостей кода, чтобы выполнить только релевантные тесты, экономя до 90% времени.
Проблема, которую решает testmon:
В проектах с 1000+ тестами их полный запуск становится узким местом в разработке. Классические методы вроде pytest -k или ручного указания файлов неэффективны и подвержены ошибкам. Testmon автоматизирует этот процесс через отслеживание связей между кодовой базой и тестами.
Как работает testmon: Магия под капотом
-
Сбор метрик покрытия:
- При первом запуске
pytest --testmonинструмент выполняет ВСЕ тесты, собирая данные о том, какие строки кода были затронуты каждым тестом. - Результаты сохраняются в скрытом файле
.testmondata(SQLite-база).
- При первом запуске
-
Анализ изменений:
- При последующих запусках testmon сравнивает текущее состояние кода с предыдущей версией (через контроль версий).
- Определяет все изменённые файлы, функции и классы.
-
Интеллектуальный отбор тестов:
- С помощью собранных данных testmon вычисляет, какие тесты затрагивают изменённые участки кода.
- Запускаются ТОЛЬКО эти тесты + тесты, помеченные как “ненадёжные” (flaky).
-
Кеширование и инкрементальность:
- Результаты прогонов кешируются. Если код не менялся, тесты не запускаются повторно.
- Поддерживается инкрементальное добавление новых тестов.
Ключевые преимущества
-
Экономия времени:
# До $ pytest # 15 минут # После $ pytest --testmon # 47 секунд (если изменён 1 файл) -
Автоматизация рутины:
- Больше не нужно гадать, какие тесты запустить после
git pullили правки кода.
- Больше не нужно гадать, какие тесты запустить после
-
Совместимость с CI/CD:
- Уменьшение времени сборки на 60-80% в пайплайнах.
- Пример для GitHub Actions:
steps: - name: Run impacted tests run: pytest --testmon
-
Гибкая конфигурация:
- Исключение файлов через
.testmonignore(аналог.gitignore):legacy/* generated_code.py
- Исключение файлов через
Установка и настройка
-
Установка:
pip install pytest-testmon -
Базовое использование:
# Первый запуск (собирает данные) pytest --testmon # Последующие запуски (умный отбор) pytest --testmon -
Интеграция с pytest.ini:
[pytest] addopts = --testmon -
Игнорирование файлов: Создайте
.testmonignoreв корне проекта:/migrations/ /tests/data/* *.ipynb
Практические примеры
Сценарий 1: Изменение в бизнес-логике
Допустим, вы исправили функцию calculate_tax() в finance/utils.py. Testmon:
- Определит все тесты, покрывающие этот файл.
- Запустит только их, проигнорировав тесты для UI или API.
Сценарий 2: Рефакторинг без изменений поведения
Если правки не затронули исполняемый код (комментарии, форматирование), testmon пропустит запуск тестов.
Сценарий 3: Добавление нового теста
Новый тест будет запущен единожды, а его связи с кодом добавятся в .testmondata.
Ограничения и подводные камни
-
Динамический импорт:
Если модули загружаются черезimportlibилиexec, testmon может не отследить зависимости. -
Изменение поведения без изменения кода:
- Внешние API, обновление БД. Решение: ручной запуск с флагом
--no-testmon.
- Внешние API, обновление БД. Решение: ручной запуск с флагом
-
Ограниченная поддержка ООП:
- Наследование и полиморфизм могут требовать дополнительных тестов. Рекомендация: помечать базовые классы как “опасные” через декоратор:
@pytest.mark.testmon(always_run=True) class TestBaseAPI: ...
- Наследование и полиморфизм могут требовать дополнительных тестов. Рекомендация: помечать базовые классы как “опасные” через декоратор:
-
Конфликты с плагинами:
- Несовместимость с некоторыми плагинами (например,
pytest-randomly). Решение: отключать конфликтующие инструменты при использовании testmon.
- Несовместимость с некоторыми плагинами (например,
Интеграция с другими инструментами
-
pytest-cov (покрытие кода):
pytest --testmon --cov- Coverage-отчёт строится ТОЛЬКО для запущенных тестов. Для полной статистики раз в сутки запускайте полный тестовый прогон.
-
pytest-xdist (параллельный запуск):
pytest --testmon -n auto- Testmon автоматически распределяет отобранные тесты по потокам.
-
CI-системы:
- Кэшируйте
.testmondataмежду билдами:# GitHub Actions - name: Cache testmon data uses: actions/cache@v3 with: path: .testmondata key: testmon-${{ hashFiles('**/*.py') }}
- Кэшируйте
Альтернативы и сравнение
| Инструмент | Метод отбора | Плюсы | Минусы |
|---|---|---|---|
| pytest-testmon | Анализ покрытия кода | Высокая точность, простота | Слепые зоны в динамике |
| pytest-picked | Git-статус изменённых файлов | Простая установка | Нет связи тест-код |
| pytest-watch | Запуск при изменении файлов | Реактивность | Нет интеллектуального отбора |
| unittest —failfast | Остановка после первой ошибки | Не требует настройки | Экономия времени минимальна |
Заключение: Когда и как внедрять testmon?
Идеальные кандидаты:
- Проекты с >200 тестами и временем прогона >5 минут.
- Команды, практикующие TDD/частыe рефакторинги.
- CI/CD пайплайны с длительным временем выполнения.
Стратегия внедрения:
- Начните с установки в режиме “только сбор данных”:
pytest --testmon --no-combined - Проведите 2-3 полных прогона для формирования базы.
- Перейдите на умный запуск в CI и локально.
- Добавьте в
.testmonignoreгенерируемые файлы. - Настройте кеширование данных в CI.
Философский итог: testmon не заменяет тесты, а делает их выполнение осмысленным. Как сказал Кент Бек:
“Тесты — это не роскошь, а инструмент выживания в хаосе разработки. Оптимизируйте их, но не жертвуйте надёжностью”.
# Ваша новая команда для повседневной работы
pytest --testmon -xvv # Быстро, с детализированным выводом и остановкой при первой ошибке