pytest-testmon — это интеллектуальный плагин для pytest, который автоматически определяет, какие тесты нужно запустить после изменений кода. Вместо полного прогона всех тестов каждый раз (что может занимать часы в крупных проектах), testmon использует анализ зависимостей кода, чтобы выполнить только релевантные тесты, экономя до 90% времени.
Проблема, которую решает testmon:
В проектах с 1000+ тестами их полный запуск становится узким местом в разработке. Классические методы вроде pytest -k или ручного указания файлов неэффективны и подвержены ошибкам. Testmon автоматизирует этот процесс через отслеживание связей между кодовой базой и тестами.
Как работает testmon: Магия под капотом
1. Сбор метрик покрытия:
- При первом запуске pytest --testmon инструмент выполняет ВСЕ тесты, собирая данные о том, какие строки кода были затронуты каждым тестом.
- Результаты сохраняются в скрытом файле .testmondata (SQLite-база).
2. Анализ изменений:
- При последующих запусках testmon сравнивает текущее состояние кода с предыдущей версией (через контроль версий).
- Определяет все изменённые файлы, функции и классы.
3. Интеллектуальный отбор тестов:
- С помощью собранных данных testmon вычисляет, какие тесты затрагивают изменённые участки кода.
- Запускаются ТОЛЬКО эти тесты + тесты, помеченные как "ненадёжные" (flaky).
4. Кеширование и инкрементальность:
- Результаты прогонов кешируются. Если код не менялся, тесты не запускаются повторно.
- Поддерживается инкрементальное добавление новых тестов.
Ключевые преимущества
1. Экономия времени:
# До
$ pytest # 15 минут
# После
$ pytest --testmon # 47 секунд (если изменён 1 файл)
2. Автоматизация рутины:
- Больше не нужно гадать, какие тесты запустить после git pull или правки кода.
3. Совместимость с CI/CD:
- Уменьшение времени сборки на 60-80% в пайплайнах.
- Пример для GitHub Actions:
steps:
..- name: Run impacted tests
....run: pytest --testmon
4. Гибкая конфигурация:
- Исключение файлов через .testmonignore (аналог .gitignore):
legacy/*
generated_code.py
Установка и настройка
1. Установка:
pip install pytest-testmon
2. Базовое использование:
# Первый запуск (собирает данные)
pytest --testmon
# Последующие запуски (умный отбор)
pytest --testmon
3. Интеграция с pytest.ini:
[pytest]
addopts = --testmon
4. Игнорирование файлов:
Создайте .testmonignore в корне проекта:
/migrations/
/tests/data/*
*.ipynb
Практические примеры
Сценарий 1: Изменение в бизнес-логике
Допустим, вы исправили функцию calculate_tax() в finance/utils.py. Testmon:
- Определит все тесты, покрывающие этот файл.
- Запустит только их, проигнорировав тесты для UI или API.
Сценарий 2: Рефакторинг без изменений поведения
Если правки не затронули исполняемый код (комментарии, форматирование), testmon пропустит запуск тестов.
Сценарий 3: Добавление нового теста
Новый тест будет запущен единожды, а его связи с кодом добавятся в .testmondata.
Ограничения и подводные камни
1. Динамический импорт:
Если модули загружаются через importlib или exec, testmon может не отследить зависимости.
2. Изменение поведения без изменения кода:
- Внешние API, обновление БД. Решение: ручной запуск с флагом --no-testmon.
3. Ограниченная поддержка ООП:
- Наследование и полиморфизм могут требовать дополнительных тестов. Рекомендация: помечать базовые классы как "опасные" через декоратор:
@pytest.mark.testmon(always_run=True)
class TestBaseAPI:
...
4. Конфликты с плагинами:
- Несовместимость с некоторыми плагинами (например, `pytest-randomly`). Решение: отключать конфликтующие инструменты при использовании testmon.
Интеграция с другими инструментами
1. pytest-cov (покрытие кода):
pytest --testmon --cov
- Coverage-отчёт строится ТОЛЬКО для запущенных тестов. Для полной статистики раз в сутки запускайте полный тестовый прогон.
2. pytest-xdist (параллельный запуск):
pytest --testmon -n auto
- Testmon автоматически распределяет отобранные тесты по потокам.
3. 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 пайплайны с длительным временем выполнения.
Стратегия внедрения:
1. Начните с установки в режиме "только сбор данных":
pytest --testmon --no-combined
2. Проведите 2-3 полных прогона для формирования базы.
3. Перейдите на умный запуск в CI и локально.
4. Добавьте в .testmonignore генерируемые файлы.
5. Настройте кеширование данных в CI.
Философский итог: testmon не заменяет тесты, а делает их выполнение осмысленным. Как сказал Кент Бек:
"Тесты — это не роскошь, а инструмент выживания в хаосе разработки. Оптимизируйте их, но не жертвуйте надёжностью".
# Ваша новая команда для повседневной работы
pytest --testmon -xvv # Быстро, с детализированным выводом и остановкой при первой ошибке
Официальная документация Примеры конфигов
Подписывайтесь:
Телеграм https://t.me/lets_go_code
Канал "Просто о программировании" https://dzen.ru/lets_go_code