Как запускать тесты в pytest: 7 способов фильтрации для экономии времени
Для кого эта статья:
- Разработчики программного обеспечения, работающие с тестированием
- QA-инженеры и тестировщики программного обеспечения
Специалисты, желающие освоить или улучшить навыки в автоматизированном тестировании с использованием pytest
В проекте с сотней тестов запустить все и ждать полчаса – роскошь, которую не может позволить себе ни один разработчик. Когда баг найден в конкретном модуле или нужно проверить только исправленную функциональность, способность точечно выбирать нужные тесты становится критически важным навыком. pytest предлагает целый арсенал инструментов для эффективного таргетирования тестов, экономя ваше время и вычислительные ресурсы. Давайте разберем семь проверенных способов, которые превратят вас из новичка, ожидающего выполнения всего тестового набора, в мастера точечных запусков. 🎯
Хотите освоить профессиональные навыки тестирования, включая работу с pytest и другими передовыми инструментами? Курс тестировщика ПО от Skypro предлагает глубокое погружение в практику автоматизированного тестирования под руководством экспертов, ежедневно использующих эти инструменты в реальных проектах. Вы не только освоите pytest, но и научитесь создавать эффективные тестовые стратегии, повышающие качество продукта и ускоряющие разработку.
Основы фильтрации тестов в pytest: почему это важно
Представьте, что у вас есть тестовый набор из 500 тестов, выполнение которого занимает 45 минут. При работе над небольшим изменением в определенном компоненте запускать все тесты каждый раз — неоправданная трата времени. Именно поэтому фильтрация и выборочный запуск тестов становятся критически важными навыками для современного разработчика. 🕒
Фильтрация тестов в pytest решает несколько ключевых проблем:
- Экономия времени — запуск только необходимых тестов значительно сокращает время обратной связи
- Фокусировка внимания — позволяет сосредоточиться на конкретной функциональности или компоненте
- Изоляция проблем — помогает быстрее локализовать источник ошибки
- Оптимизация CI/CD — возможность настроить параллельное выполнение разных групп тестов
Рассмотрим сравнительную эффективность различных подходов к запуску тестов:
| Подход | Время | Ресурсы | Эффективность |
|---|---|---|---|
| Запуск всех тестов | Высокое | Максимальные | Низкая при локальной разработке |
| Фильтрация по имени | Низкое | Минимальные | Высокая для точечных изменений |
| Фильтрация по маркерам | Среднее | Средние | Высокая для компонентных изменений |
| Фильтрация по модулям | Среднее | Средние | Оптимальная для работы с модулями |
Алексей Смирнов, Lead QA Engineer
В проекте с микросервисной архитектурой наша тестовая база разрослась до 3000+ тестов, и полный прогон занимал около 2 часов. Это стало настоящей проблемой, особенно когда требовалось быстро подтвердить исправление конкретного бага.
Мы внедрили систему маркеров, классифицирующую тесты по компонентам, приоритету и типу. Настроили Jenkins так, чтобы разработчики могли указывать конкретные фильтры для своих коммитов. Результат превзошел ожидания — среднее время получения обратной связи сократилось с часов до минут, а количество пропущенных регрессий не увеличилось, поскольку полное тестирование по-прежнему выполнялось перед релизом.
Ключевым открытием стало то, что разработчики начали писать больше тестов, когда поняли, что могут запускать только релевантные для их задачи. Это улучшило и покрытие кода, и общее качество продукта.

Запуск тестов по названию и шаблонам с опцией -k
Опция -k — это, пожалуй, самый мощный инструмент точечного запуска тестов в арсенале pytest. Она позволяет фильтровать тесты на основе их имен, используя выражения, похожие на Python-выражения. Это особенно удобно, когда вам нужно быстро проверить конкретную функциональность без необходимости создавать специальные маркеры. 🔍
Базовое использование выглядит следующим образом:
pytest -k "имя_теста"
Но настоящая мощь этого параметра раскрывается при использовании логических операторов:
pytest -k "login and not slow"— запустит все тесты, содержащие "login" в имени, но не содержащие "slow"pytest -k "api or integration"— запустит тесты, содержащие "api" или "integration" в имениpytest -k "user and (create or delete)"— запустит тесты, содержащие "user" и либо "create", либо "delete" в имени
Рассмотрим несколько практических примеров использования опции -k:
| Команда | Что запустит | Когда использовать |
|---|---|---|
pytest -k test_user_login | Любой тест, содержащий "testuserlogin" в имени | При работе над конкретной функцией |
pytest -k "login and not integration" | Тесты с "login", исключая интеграционные | Для быстрого модульного тестирования |
pytest -k "auth or permission" | Все тесты аутентификации и прав доступа | При изменениях в системе безопасности |
pytest -k "user and (create or update)" | Тесты создания и обновления пользователя | При работе с методами пользовательского API |
Одна из наиболее полезных техник — комбинирование -k с другими опциями. Например:
pytest -k "api" -v --lf
Эта команда запустит все тесты, содержащие "api" в имени, в подробном режиме и ограничится только теми, которые не прошли в предыдущем запуске (--lf — last failed).
Существуют и некоторые ограничения использования -k:
- Требуется осмысленная система именования тестов
- Сложные логические выражения могут быть трудны для чтения
- При больших наборах тестов поиск может занять время
Для максимальной эффективности рекомендуется придерживаться последовательной стратегии именования тестов, например: test_[компонент]_[функция]_[сценарий], что сделает фильтрацию по -k более предсказуемой и полезной.
Маркеры в pytest: организация и выбор тестов по тегам
Маркеры в pytest представляют собой систему тегирования, позволяющую категоризировать тесты независимо от их имен или расположения. Это мощный механизм организации тестов по функциональным группам, приоритету, типу или любой другой логической классификации. 🏷️
Для маркировки теста используется декоратор @pytest.mark:
@pytest.mark.slow
def test_complex_calculation():
# Тест, который выполняется долго
...
Запуск тестов с определенным маркером выполняется с помощью опции -m:
pytest -m slow
Стандартные маркеры pytest включают:
@pytest.mark.skip— пропустить тест@pytest.mark.skipif— пропустить тест при определенном условии@pytest.mark.xfail— пометить тест как ожидаемо падающий@pytest.mark.parametrize— параметризовать тест
Но настоящая сила маркеров раскрывается при создании собственных категорий. Рассмотрим несколько сценариев использования:
Марина Петрова, QA Automation Lead
В нашем проекте по разработке финансового ПО мы столкнулись с проблемой — CI-пайплайн занимал 4 часа из-за более чем 7000 тестов. Это блокировало быстрые исправления и замедляло весь процесс разработки.
Мы разработали систему маркеров с тремя основными измерениями: по бизнес-доменам (payments, accounts, reporting), по типу тестов (unit, integration, e2e) и по приоритету (p0, p1, p2). Для интеграции в CI мы создали несколько профилей запуска:
- Quick — только юнит-тесты приоритета p0 (5 минут)
- Standard — все тесты p0 и интеграционные p1 (30 минут)
- Full — все тесты (запускаются ночью)
Это преобразование позволило разработчикам получать быструю обратную связь на свои изменения, выбирая профиль в зависимости от характера изменений. Например, при исправлении бага в платежном модуле можно было запустить pytest -m "payments and p0", что занимало всего несколько минут.
Интересно, что после внедрения системы маркеров мы обнаружили, что около 20% наших тестов были избыточными или устаревшими, что позволило дополнительно оптимизировать нашу тестовую базу.
Для эффективного использования маркеров важно зарегистрировать их в файле конфигурации pytest (обычно pytest.ini, pyproject.toml или conftest.py):
# В pytest.ini
[pytest]
markers =
slow: тесты, выполняющиеся долго
api: тесты API
security: тесты безопасности
ui: тесты пользовательского интерфейса
Логические операции с маркерами работают аналогично опции -k:
pytest -m "api and not slow"
pytest -m "security or ui"
Можно также комбинировать маркеры непосредственно в коде, применяя несколько декораторов к одному тесту:
@pytest.mark.api
@pytest.mark.security
def test_api_authentication():
# Тест аутентификации API
...
Для более сложной организации тестов можно использовать параметризованные маркеры:
@pytest.mark.component("auth")
@pytest.mark.priority(1)
def test_important_login_feature():
# Тест важной функции входа
...
И затем запускать их с помощью более сложных селекторов:
pytest -m "component('auth') and priority(1)"
При работе с большими проектами рекомендуется разработать стратегию маркировки тестов, согласованную со всей командой, чтобы избежать хаоса и дублирования.
Выбор тестов по расположению и структуре проекта
Структура проекта — еще один мощный способ организации и выбора тестов в pytest. Правильное размещение тестовых файлов позволяет интуитивно и быстро выбирать нужные наборы тестов для запуска. 📂
Базовый запуск тестов по директориям и файлам выглядит просто:
pytest path/to/test_file.py # Запуск всех тестов в конкретном файле
pytest path/to/test_directory/ # Запуск всех тестов в директории
Для более точечного выбора можно указывать конкретные тесты через двоеточие:
pytest path/to/test_file.py::test_specific_function # Запуск конкретного теста
pytest path/to/test_file.py::TestClass::test_method # Запуск метода в тестовом классе
При организации тестов следует учитывать несколько ключевых принципов:
- Модульная структура — тесты должны отражать структуру тестируемого кода
- Иерархия сложности — от простых юнит-тестов к сложным интеграционным
- Изоляция окружения — тесты, требующие специфического окружения, в отдельных директориях
- Согласованное именование — префикс "test_" для файлов и функций
Типичная структура тестового каталога может выглядеть так:
tests/
├── unit/ # Модульные тесты
│ ├── test_models.py
│ └── test_utils.py
├── integration/ # Интеграционные тесты
│ ├── test_api.py
│ └── test_db.py
├── functional/ # Функциональные тесты
│ └── test_workflows.py
├── performance/ # Тесты производительности
│ └── test_load.py
└── conftest.py # Общие фикстуры и настройки
С такой структурой легко запускать разные типы тестов:
pytest tests/unit/ # Только модульные тесты
pytest tests/integration/ tests/functional/ # Интеграционные и функциональные тесты
Для проектов с множеством компонентов можно организовать тесты по компонентам:
tests/
├── auth/ # Тесты аутентификации
│ ├── unit/
│ └── integration/
├── billing/ # Тесты биллинга
│ ├── unit/
│ └── integration/
└── conftest.py
Такая структура позволяет удобно запускать тесты для отдельных компонентов:
pytest tests/auth/
Можно использовать подстановки для более гибкого выбора тестов:
pytest tests/**/test_*login*.py # Все тесты, связанные с логином, в любых подкаталогах
Один из продвинутых подходов — использование файла conftest.py для настройки среды тестирования для разных директорий. Например, можно определить разные фикстуры для разных типов тестов:
# tests/unit/conftest.py
@pytest.fixture
def db():
return MockDatabase() # Мок для юнит-тестов
# tests/integration/conftest.py
@pytest.fixture
def db():
return RealTestDatabase() # Реальная тестовая БД для интеграционных тестов
Такая организация особенно полезна при работе в команде, так как создает интуитивно понятную структуру, которая автоматически подсказывает, где должны находиться тесты для конкретных компонентов.
Продвинутые техники оптимизации запуска test-suite
Для масштабных проектов с тысячами тестов базовых методов фильтрации может быть недостаточно. Здесь вступают в игру продвинутые техники, позволяющие радикально оптимизировать процесс тестирования. 🚀
Рассмотрим самые эффективные стратегии:
1. Запуск только упавших тестов
Одна из самых полезных функций pytest — возможность запускать только те тесты, которые не прошли в предыдущем запуске:
pytest --lf # Запуск только упавших тестов (last-failed)
pytest --ff # Сначала запустить упавшие тесты, затем остальные (failed-first)
Это особенно полезно при исправлении ошибок, когда нужно быстро проверить, решена ли проблема.
2. Оптимизация на основе изменений в коде
Плагин pytest-testmon анализирует изменения в коде и автоматически запускает только те тесты, которые могут быть затронуты этими изменениями:
pip install pytest-testmon
pytest --testmon
Это может сократить время тестирования на 90% при точечных изменениях в крупных проектах.
3. Параллельное выполнение тестов
Для многоядерных систем параллельное выполнение тестов с помощью pytest-xdist может значительно ускорить процесс:
pip install pytest-xdist
pytest -n auto # Автоопределение количества процессов
pytest -n 4 # Явное указание 4 параллельных процессов
Это особенно эффективно для IO-bound тестов (работающих с сетью или файловой системой).
4. Умное пропускание тестов
Использование skipif с динамическими условиями позволяет автоматически пропускать тесты, которые не имеют смысла в текущем контексте:
@pytest.mark.skipif(
os.environ.get("TEST_ENV") == "ci",
reason="Skipped in CI environment"
)
def test_local_only_feature():
# Тест, имеющий смысл только локально
...
Сравнение продвинутых стратегий оптимизации:
| Техника | Преимущества | Ограничения | Лучший сценарий использования |
|---|---|---|---|
| --lf / --ff | Простота, встроенная функция | Работает только с историей предыдущих запусков | Исправление конкретных багов |
| testmon | Интеллектуальный отбор тестов | Сложная настройка, возможны ложные пропуски | Непрерывная разработка с частыми изменениями |
| xdist | Значительное ускорение | Тесты должны быть изолированы, сложности отладки | Большие наборы независимых тестов |
| Умное пропускание | Контекстная адаптация | Требует тщательной настройки условий | Мультиплатформенные проекты |
5. Кеширование результатов тестирования
Для тестов, выполнение которых занимает значительное время, можно использовать кеширование результатов:
# Требуется плагин pytest-cache
@pytest.mark.cacheable
def test_expensive_operation():
# Дорогостоящая операция
...
6. Создание пользовательских селекторов
Определение собственных правил отбора тестов в conftest.py может создать мощные, специфичные для проекта механизмы фильтрации:
# В conftest.py
def pytest_runtest_setup(item):
# Пропускать медленные тесты при быстром режиме
if item.config.getoption("--fast") and "slow" in item.keywords:
pytest.skip("skipped in fast mode")
def pytest_addoption(parser):
parser.addoption("--fast", action="store_true", help="run only fast tests")
Затем использование:
pytest --fast
7. Интеллектуальное распределение тестов
Для CI/CD можно использовать стратегии распределения тестов на основе их истории выполнения:
pytest --splits 5 --group 2 # Выполнить вторую группу из 5 равных по времени групп
Это позволяет равномерно распределить нагрузку между несколькими CI-агентами, сократив общее время выполнения.
Наиболее эффективный подход — комбинирование нескольких стратегий. Например, для разработки можно использовать:
pytest --testmon -xvs
А для CI-пайплайнов:
pytest -n auto -m "not slow" --junitxml=results.xml
Адаптация этих техник к конкретному проекту может сократить время тестирования с часов до минут, значительно повышая производительность команды разработки.
Выбор правильной стратегии запуска тестов — это не просто техническое решение, а тактическое преимущество в современной разработке. Овладев всеми семью методами фильтрации в pytest, вы получаете контроль над тестовым процессом, превращая его из препятствия в инструмент ускорения разработки. Помните, что идеальная стратегия тестирования должна эволюционировать вместе с проектом — начните с простых фильтров по имени для небольших проектов и постепенно внедряйте более продвинутые техники по мере роста кодовой базы. Хороший тестовый набор — это тот, который не только находит ошибки, но и делает это максимально эффективно.