Как запускать тесты в pytest: 7 способов фильтрации для экономии времени

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Разработчики программного обеспечения, работающие с тестированием
  • QA-инженеры и тестировщики программного обеспечения
  • Специалисты, желающие освоить или улучшить навыки в автоматизированном тестировании с использованием pytest

    В проекте с сотней тестов запустить все и ждать полчаса – роскошь, которую не может позволить себе ни один разработчик. Когда баг найден в конкретном модуле или нужно проверить только исправленную функциональность, способность точечно выбирать нужные тесты становится критически важным навыком. pytest предлагает целый арсенал инструментов для эффективного таргетирования тестов, экономя ваше время и вычислительные ресурсы. Давайте разберем семь проверенных способов, которые превратят вас из новичка, ожидающего выполнения всего тестового набора, в мастера точечных запусков. 🎯

Хотите освоить профессиональные навыки тестирования, включая работу с pytest и другими передовыми инструментами? Курс тестировщика ПО от Skypro предлагает глубокое погружение в практику автоматизированного тестирования под руководством экспертов, ежедневно использующих эти инструменты в реальных проектах. Вы не только освоите pytest, но и научитесь создавать эффективные тестовые стратегии, повышающие качество продукта и ускоряющие разработку.

Основы фильтрации тестов в pytest: почему это важно

Представьте, что у вас есть тестовый набор из 500 тестов, выполнение которого занимает 45 минут. При работе над небольшим изменением в определенном компоненте запускать все тесты каждый раз — неоправданная трата времени. Именно поэтому фильтрация и выборочный запуск тестов становятся критически важными навыками для современного разработчика. 🕒

Фильтрация тестов в pytest решает несколько ключевых проблем:

  • Экономия времени — запуск только необходимых тестов значительно сокращает время обратной связи
  • Фокусировка внимания — позволяет сосредоточиться на конкретной функциональности или компоненте
  • Изоляция проблем — помогает быстрее локализовать источник ошибки
  • Оптимизация CI/CD — возможность настроить параллельное выполнение разных групп тестов

Рассмотрим сравнительную эффективность различных подходов к запуску тестов:

Подход Время Ресурсы Эффективность
Запуск всех тестов Высокое Максимальные Низкая при локальной разработке
Фильтрация по имени Низкое Минимальные Высокая для точечных изменений
Фильтрация по маркерам Среднее Средние Высокая для компонентных изменений
Фильтрация по модулям Среднее Средние Оптимальная для работы с модулями

Алексей Смирнов, Lead QA Engineer

В проекте с микросервисной архитектурой наша тестовая база разрослась до 3000+ тестов, и полный прогон занимал около 2 часов. Это стало настоящей проблемой, особенно когда требовалось быстро подтвердить исправление конкретного бага.

Мы внедрили систему маркеров, классифицирующую тесты по компонентам, приоритету и типу. Настроили Jenkins так, чтобы разработчики могли указывать конкретные фильтры для своих коммитов. Результат превзошел ожидания — среднее время получения обратной связи сократилось с часов до минут, а количество пропущенных регрессий не увеличилось, поскольку полное тестирование по-прежнему выполнялось перед релизом.

Ключевым открытием стало то, что разработчики начали писать больше тестов, когда поняли, что могут запускать только релевантные для их задачи. Это улучшило и покрытие кода, и общее качество продукта.

Пошаговый план для смены профессии

Запуск тестов по названию и шаблонам с опцией -k

Опция -k — это, пожалуй, самый мощный инструмент точечного запуска тестов в арсенале pytest. Она позволяет фильтровать тесты на основе их имен, используя выражения, похожие на Python-выражения. Это особенно удобно, когда вам нужно быстро проверить конкретную функциональность без необходимости создавать специальные маркеры. 🔍

Базовое использование выглядит следующим образом:

Bash
Скопировать код
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 с другими опциями. Например:

Bash
Скопировать код
pytest -k "api" -v --lf

Эта команда запустит все тесты, содержащие "api" в имени, в подробном режиме и ограничится только теми, которые не прошли в предыдущем запуске (--lf — last failed).

Существуют и некоторые ограничения использования -k:

  • Требуется осмысленная система именования тестов
  • Сложные логические выражения могут быть трудны для чтения
  • При больших наборах тестов поиск может занять время

Для максимальной эффективности рекомендуется придерживаться последовательной стратегии именования тестов, например: test_[компонент]_[функция]_[сценарий], что сделает фильтрацию по -k более предсказуемой и полезной.

Маркеры в pytest: организация и выбор тестов по тегам

Маркеры в pytest представляют собой систему тегирования, позволяющую категоризировать тесты независимо от их имен или расположения. Это мощный механизм организации тестов по функциональным группам, приоритету, типу или любой другой логической классификации. 🏷️

Для маркировки теста используется декоратор @pytest.mark:

Python
Скопировать код
@pytest.mark.slow
def test_complex_calculation():
# Тест, который выполняется долго
...

Запуск тестов с определенным маркером выполняется с помощью опции -m:

Bash
Скопировать код
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 мы создали несколько профилей запуска:

  1. Quick — только юнит-тесты приоритета p0 (5 минут)
  2. Standard — все тесты p0 и интеграционные p1 (30 минут)
  3. Full — все тесты (запускаются ночью)

Это преобразование позволило разработчикам получать быструю обратную связь на свои изменения, выбирая профиль в зависимости от характера изменений. Например, при исправлении бага в платежном модуле можно было запустить pytest -m "payments and p0", что занимало всего несколько минут.

Интересно, что после внедрения системы маркеров мы обнаружили, что около 20% наших тестов были избыточными или устаревшими, что позволило дополнительно оптимизировать нашу тестовую базу.

Для эффективного использования маркеров важно зарегистрировать их в файле конфигурации pytest (обычно pytest.ini, pyproject.toml или conftest.py):

ini
Скопировать код
# В pytest.ini
[pytest]
markers =
slow: тесты, выполняющиеся долго
api: тесты API
security: тесты безопасности
ui: тесты пользовательского интерфейса

Логические операции с маркерами работают аналогично опции -k:

Bash
Скопировать код
pytest -m "api and not slow"
pytest -m "security or ui"

Можно также комбинировать маркеры непосредственно в коде, применяя несколько декораторов к одному тесту:

Python
Скопировать код
@pytest.mark.api
@pytest.mark.security
def test_api_authentication():
# Тест аутентификации API
...

Для более сложной организации тестов можно использовать параметризованные маркеры:

Python
Скопировать код
@pytest.mark.component("auth")
@pytest.mark.priority(1)
def test_important_login_feature():
# Тест важной функции входа
...

И затем запускать их с помощью более сложных селекторов:

Bash
Скопировать код
pytest -m "component('auth') and priority(1)"

При работе с большими проектами рекомендуется разработать стратегию маркировки тестов, согласованную со всей командой, чтобы избежать хаоса и дублирования.

Выбор тестов по расположению и структуре проекта

Структура проекта — еще один мощный способ организации и выбора тестов в pytest. Правильное размещение тестовых файлов позволяет интуитивно и быстро выбирать нужные наборы тестов для запуска. 📂

Базовый запуск тестов по директориям и файлам выглядит просто:

Bash
Скопировать код
pytest path/to/test_file.py # Запуск всех тестов в конкретном файле
pytest path/to/test_directory/ # Запуск всех тестов в директории

Для более точечного выбора можно указывать конкретные тесты через двоеточие:

Bash
Скопировать код
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 # Общие фикстуры и настройки

С такой структурой легко запускать разные типы тестов:

Bash
Скопировать код
pytest tests/unit/ # Только модульные тесты
pytest tests/integration/ tests/functional/ # Интеграционные и функциональные тесты

Для проектов с множеством компонентов можно организовать тесты по компонентам:

tests/
├── auth/ # Тесты аутентификации
│ ├── unit/
│ └── integration/
├── billing/ # Тесты биллинга
│ ├── unit/
│ └── integration/
└── conftest.py

Такая структура позволяет удобно запускать тесты для отдельных компонентов:

Bash
Скопировать код
pytest tests/auth/

Можно использовать подстановки для более гибкого выбора тестов:

Bash
Скопировать код
pytest tests/**/test_*login*.py # Все тесты, связанные с логином, в любых подкаталогах

Один из продвинутых подходов — использование файла conftest.py для настройки среды тестирования для разных директорий. Например, можно определить разные фикстуры для разных типов тестов:

Python
Скопировать код
# tests/unit/conftest.py
@pytest.fixture
def db():
return MockDatabase() # Мок для юнит-тестов

# tests/integration/conftest.py
@pytest.fixture
def db():
return RealTestDatabase() # Реальная тестовая БД для интеграционных тестов

Такая организация особенно полезна при работе в команде, так как создает интуитивно понятную структуру, которая автоматически подсказывает, где должны находиться тесты для конкретных компонентов.

Продвинутые техники оптимизации запуска test-suite

Для масштабных проектов с тысячами тестов базовых методов фильтрации может быть недостаточно. Здесь вступают в игру продвинутые техники, позволяющие радикально оптимизировать процесс тестирования. 🚀

Рассмотрим самые эффективные стратегии:

1. Запуск только упавших тестов

Одна из самых полезных функций pytest — возможность запускать только те тесты, которые не прошли в предыдущем запуске:

Bash
Скопировать код
pytest --lf # Запуск только упавших тестов (last-failed)
pytest --ff # Сначала запустить упавшие тесты, затем остальные (failed-first)

Это особенно полезно при исправлении ошибок, когда нужно быстро проверить, решена ли проблема.

2. Оптимизация на основе изменений в коде

Плагин pytest-testmon анализирует изменения в коде и автоматически запускает только те тесты, которые могут быть затронуты этими изменениями:

Bash
Скопировать код
pip install pytest-testmon
pytest --testmon

Это может сократить время тестирования на 90% при точечных изменениях в крупных проектах.

3. Параллельное выполнение тестов

Для многоядерных систем параллельное выполнение тестов с помощью pytest-xdist может значительно ускорить процесс:

Bash
Скопировать код
pip install pytest-xdist
pytest -n auto # Автоопределение количества процессов
pytest -n 4 # Явное указание 4 параллельных процессов

Это особенно эффективно для IO-bound тестов (работающих с сетью или файловой системой).

4. Умное пропускание тестов

Использование skipif с динамическими условиями позволяет автоматически пропускать тесты, которые не имеют смысла в текущем контексте:

Python
Скопировать код
@pytest.mark.skipif(
os.environ.get("TEST_ENV") == "ci",
reason="Skipped in CI environment"
)
def test_local_only_feature():
# Тест, имеющий смысл только локально
...

Сравнение продвинутых стратегий оптимизации:

Техника Преимущества Ограничения Лучший сценарий использования
--lf / --ff Простота, встроенная функция Работает только с историей предыдущих запусков Исправление конкретных багов
testmon Интеллектуальный отбор тестов Сложная настройка, возможны ложные пропуски Непрерывная разработка с частыми изменениями
xdist Значительное ускорение Тесты должны быть изолированы, сложности отладки Большие наборы независимых тестов
Умное пропускание Контекстная адаптация Требует тщательной настройки условий Мультиплатформенные проекты

5. Кеширование результатов тестирования

Для тестов, выполнение которых занимает значительное время, можно использовать кеширование результатов:

Python
Скопировать код
# Требуется плагин pytest-cache
@pytest.mark.cacheable
def test_expensive_operation():
# Дорогостоящая операция
...

6. Создание пользовательских селекторов

Определение собственных правил отбора тестов в conftest.py может создать мощные, специфичные для проекта механизмы фильтрации:

Python
Скопировать код
# В 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")

Затем использование:

Bash
Скопировать код
pytest --fast

7. Интеллектуальное распределение тестов

Для CI/CD можно использовать стратегии распределения тестов на основе их истории выполнения:

Bash
Скопировать код
pytest --splits 5 --group 2 # Выполнить вторую группу из 5 равных по времени групп

Это позволяет равномерно распределить нагрузку между несколькими CI-агентами, сократив общее время выполнения.

Наиболее эффективный подход — комбинирование нескольких стратегий. Например, для разработки можно использовать:

Bash
Скопировать код
pytest --testmon -xvs

А для CI-пайплайнов:

Bash
Скопировать код
pytest -n auto -m "not slow" --junitxml=results.xml

Адаптация этих техник к конкретному проекту может сократить время тестирования с часов до минут, значительно повышая производительность команды разработки.

Выбор правильной стратегии запуска тестов — это не просто техническое решение, а тактическое преимущество в современной разработке. Овладев всеми семью методами фильтрации в pytest, вы получаете контроль над тестовым процессом, превращая его из препятствия в инструмент ускорения разработки. Помните, что идеальная стратегия тестирования должна эволюционировать вместе с проектом — начните с простых фильтров по имени для небольших проектов и постепенно внедряйте более продвинутые техники по мере роста кодовой базы. Хороший тестовый набор — это тот, который не только находит ошибки, но и делает это максимально эффективно.

Загрузка...