Перейти к содержимому
К программе курса
Pytest: Профессиональные инструменты
5 / 863%

Custom markers и strict mode

15 минут

Коллега написал @pytest.mark.slоw (с опечаткой). Тесты запустились, но маркер не сработал. Как защититься от таких ошибок?

Цель: Создать собственные маркеры и настроить strict mode для защиты от опечаток.

Вы точно готовы?

Убедитесь, что умеете:

# Использовать встроенные маркеры
@pytest.mark.skip
def test_skip():
    pass
 
@pytest.mark.parametrize("value", [1, 2])
def test_param(value):
    assert value > 0

Если базовые маркеры непонятны — вернитесь к pytest-basics урок 5.

Проблема: неизвестные маркеры

Опечатка в маркере

# tests/test_api.py
 
@pytest.mark.slow  # ✅ Правильно
def test_api_1():
    pass
 
@pytest.mark.slоw  # ❌ Опечатка (кириллическая 'о')
def test_api_2():
    pass
 
@pytest.mark.sloww  # ❌ Опечатка (две 'w')
def test_api_3():
    pass

Запуск:

pytest -m slow

Результат:

collected 3 items / 2 deselected / 1 selected
 
tests/test_api.py::test_api_1 PASSED
 
PytestUnknownMarkWarning: Unknown pytest.mark.slоw
PytestUnknownMarkWarning: Unknown pytest.mark.sloww

Тесты с опечатками не запустились! Но предупреждение легко пропустить.

Решение: strict markers

Регистрация маркеров в pytest.ini

# pytest.ini
 
[pytest]
markers =
    slow: Медленные тесты (> 5 секунд)
    integration: Integration тесты с внешними системами
    smoke: Smoke тесты для быстрой проверки
    unit: Unit тесты (изолированные)

Включение strict mode

# pytest.ini
 
[pytest]
addopts =
    --strict-markers  # Ошибка на неизвестных маркерах
 
markers =
    slow: Медленные тесты
    integration: Integration тесты

Теперь:

pytest

Результат:

ERROR: Unknown marker 'slоw' - is this a typo?

Опечатка превратилась в ошибку! Тесты не запустятся пока не исправим.

Альтернатива: CLI флаг

# Разово включить strict markers
pytest --strict-markers
 
# Разово отключить
pytest --strict-markers=False

Создание кастомных маркеров

Маркеры с документацией

# pytest.ini
 
[pytest]
markers =
    # По скорости
    slow: Медленные тесты (> 5 секунд). Используйте pytest -m "not slow" для быстрого запуска.
    fast: Быстрые тесты (< 1 секунды)
 
    # По типу
    unit: Unit-тесты (изолированные, без внешних зависимостей)
    integration: Integration-тесты (БД, API, файлы)
    e2e: End-to-end тесты (полный цикл)
 
    # По зависимостям
    requires_db: Требует PostgreSQL
    requires_redis: Требует Redis
    requires_docker: Требует Docker
    requires_network: Требует доступ в интернет
 
    # По функциональности
    auth: Тесты аутентификации/авторизации
    payments: Тесты платежной системы
    api: Тесты API endpoints
    cli: Тесты CLI команд
 
    # Специальные
    smoke: Критичные тесты для smoke testing
    regression: Regression тесты (проверка старых багов)
    wip: Work In Progress (в разработке, могут падать)

Просмотр маркеров:

pytest --markers

Результат:

@pytest.mark.slow: Медленные тесты (> 5 секунд). Используйте pytest -m "not slow"...
@pytest.mark.unit: Unit-тесты (изолированные, без внешних зависимостей)
...

Использование маркеров

import pytest
 
@pytest.mark.slow
@pytest.mark.integration
@pytest.mark.requires_db
def test_complex_query():
    """Сложный запрос к БД"""
    pass
 
@pytest.mark.unit
@pytest.mark.fast
def test_simple_calculation():
    """Простой расчёт"""
    pass
 
@pytest.mark.smoke
@pytest.mark.api
def test_api_health():
    """Health check API"""
    pass

Запуск:

# Только быстрые unit тесты
pytest -m "unit and fast"
 
# Интеграционные, но не медленные
pytest -m "integration and not slow"
 
# Smoke тесты
pytest -m smoke

Маркеры с аргументами

Skip с причиной

@pytest.mark.skip(reason="Waiting for API v2")
def test_api_v2():
    pass
 
@pytest.mark.skipif(sys.platform == "win32", reason="Unix only")
def test_unix_specific():
    pass

Кастомные маркеры с аргументами

# Маркер с таймаутом
@pytest.mark.timeout(seconds=10)
def test_slow_operation():
    pass
 
# Маркер с приоритетом
@pytest.mark.priority("high")
def test_critical_feature():
    pass
 
# Маркер с багом
@pytest.mark.bug(issue="JIRA-123")
def test_regression():
    pass

Доступ к аргументам:

# conftest.py
 
def pytest_collection_modifyitems(items):
    """Сортируем тесты по приоритету"""
    def priority_key(item):
        marker = item.get_closest_marker("priority")
        if marker:
            priorities = {"high": 1, "medium": 2, "low": 3}
            return priorities.get(marker.args[0], 99)
        return 99
 
    items.sort(key=priority_key)

Best practices (bonus)

1. Согласованность команды

# pytest.ini для всей команды
 
[pytest]
# Защита от опечаток
addopts = --strict-markers
 
markers =
    # Используйте префиксы для группировки
    type_unit: Unit тесты
    type_integration: Integration тесты
    type_e2e: E2E тесты
 
    speed_slow: Медленные
    speed_fast: Быстрые
 
    dep_db: Требует БД
    dep_redis: Требует Redis

2. Минимальный набор маркеров

# ✅ ХОРОШО — минимально необходимые
markers =
    slow: Медленные тесты
    integration: Integration тесты
    smoke: Smoke тесты
 
# ❌ ПЛОХО — слишком много
markers =
    very_slow:
    moderately_slow:
    slightly_slow:
    ...  # 50 маркеров

3. Документация в README

## Маркеры тестов
 
- `@pytest.mark.slow` — медленные тесты (> 5с)
  ```bash
  pytest -m "not slow"  # Пропустить медленные
  ```
  • @pytest.mark.smoke — критичные тесты
    pytest -m smoke  # Быстрая проверка

Что вы изучили

  • --strict-markers — защита от опечаток в маркерах
  • Регистрация — markers в pytest.ini
  • Документация — описание каждого маркера
  • Аргументы — маркеры с параметрами
  • Best practices — минимум маркеров, группировка, документация

Следующий урок

Отлично! Маркеры настроены и защищены от опечаток. Но что если нужно создать собственный pytest плагин для автоматизации команды?

Переходите к уроку 5: Создание pytest-плагинов

В следующем уроке вы узнаете:

  • Hooks для расширения pytest
  • conftest.py как плагин
  • pytest_configure, pytest_collection_modifyitems
  • Создание пакета плагина

Устранение неисправностей

Pytest не видит конфиг.

Проверьте:

  • pytest.ini лежит в корне проекта
  • Запускаете pytest из корня (или передайте -c path/to/pytest.ini)
  • Нет опечатки в названии маркера в pytest.ini

Режим strict нужно включить.

# pytest.ini
[pytest]
addopts = --strict-markers

Либо запустите разово:

pytest --strict-markers

CLI переопределит настройку.

pytest --strict-markers=false

Используйте вызов, а не присваивание.

# ✅ ПРАВИЛЬНО
@pytest.mark.timeout(10)

# ❌ НЕПРАВИЛЬНО

@pytest.mark.timeout = 10
Custom markers и strict mode — Pytest: Профессиональные инструменты — Potapov.me