Skip to main content
Back to course
Pytest с нуля: Первые тесты за 2.5 часа
6 / 875%

Маркеры: запускаем нужные тесты

20 минут

У вас 100 тестов. 90 быстрых (< 1 сек) и 10 медленных (> 10 сек). При разработке хочется запускать только быстрые. Как?

Цель: Научиться группировать тесты с помощью маркеров и запускать только нужные подмножества.

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

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

# Запустить все тесты
pytest
 
# Запустить один файл
pytest tests/test_user.py

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

Проблема: все тесты запускаются каждый раз

Реальный сценарий:

# При разработке запускаем pytest
pytest
 
# Запускается 100 тестов, из них:
# - 90 быстрых (unit) — 10 секунд total
# - 10 медленных (integration с БД) — 50 секунд total
# Итого: 60 секунд!

Проблема: Ждать минуту после каждого изменения — слишком долго.

@pytest.mark для группировки

Базовый маркер: @pytest.mark.slow

# tests/test_user.py
 
def test_user_creation():
    """Быстрый unit-тест"""
    user = User("Alice", 25)
    assert user.name == "Alice"
    # Выполняется < 1 секунды
 
 
import pytest
 
@pytest.mark.slow
def test_user_save_to_database():
    """Медленный integration-тест с БД"""
    user = User("Alice", 25)
    user.save()  # Запись в PostgreSQL
 
    # Проверяем что сохранилось
    loaded = User.load_from_db("Alice")
    assert loaded.age == 25
    # Выполняется ~5 секунд

Запуск тестов с маркерами

Запустить ТОЛЬКО медленные тесты:

pytest -m slow

Результат:

======================== test session starts ============================
collected 2 items / 1 deselected / 1 selected
 
tests/test_user.py::test_user_save_to_database PASSED             [100%]
 
================= 1 passed, 1 deselected in 5.01s ====================

✅ Запустился только тест с @pytest.mark.slow!

Запустить ВСЁ КРОМЕ медленных тестов:

pytest -m "not slow"

Результат:

======================== test session starts ============================
collected 2 items / 1 deselected / 1 selected
 
tests/test_user.py::test_user_creation PASSED                      [100%]
 
================= 1 passed, 1 deselected in 0.01s ====================

✅ Медленный тест пропущен!

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

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

При запуске pytest:

PytestUnknownMarkWarning: Unknown pytest.mark.slow

Причина: pytest не знает о маркере slow.

Решение: зарегистрировать в pytest.ini

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

После этого предупреждения исчезнут!

Посмотреть все зарегистрированные маркеры

pytest --markers

Результат:

@pytest.mark.slow: Медленные тесты (> 5 секунд)
@pytest.mark.integration: Integration-тесты с внешними системами
@pytest.mark.unit: Unit-тесты (изолированные)
...

Практические примеры группировки

По скорости выполнения

import pytest
 
# Быстрый тест (без маркера или @pytest.mark.unit)
def test_add_numbers():
    """Unit-тест, < 1 сек"""
    assert add(2, 3) == 5
 
 
@pytest.mark.slow
def test_batch_import():
    """Импорт 10000 записей в БД"""
    import_users_from_csv("large_file.csv")
    assert User.count() == 10000
    # Выполняется ~20 секунд

Команды:

# Локально: только быстрые
pytest -m "not slow"
 
# CI: все тесты
pytest

По типу тестов

@pytest.mark.unit
def test_validate_email():
    """Unit-тест: изолированная логика"""
    assert validate_email("test@example.com") == True
 
 
@pytest.mark.integration
def test_send_email_via_smtp():
    """Integration: реальный SMTP-сервер"""
    send_email("test@example.com", "Subject", "Body")
    # Требует доступа к SMTP

Команды:

# Только unit-тесты
pytest -m unit
 
# Только integration-тесты
pytest -m integration

По функциональности

@pytest.mark.auth
def test_login_success():
    """Тесты аутентификации"""
    pass
 
 
@pytest.mark.payments
def test_process_payment():
    """Тесты платежей"""
    pass
 
 
@pytest.mark.api
def test_api_endpoint():
    """Тесты API"""
    pass

Команды:

# Только тесты аутентификации
pytest -m auth
 
# Платежи И API
pytest -m "payments or api"

Комбинирование маркеров

Несколько маркеров на одном тесте

@pytest.mark.integration
@pytest.mark.slow
@pytest.mark.requires_db
def test_complex_query():
    """Сложный запрос к БД"""
    pass

Логические операторы

# Только integration, но НЕ slow
pytest -m "integration and not slow"
 
# Либо auth, либо payments
pytest -m "auth or payments"
 
# integration, но НЕ requires_db
pytest -m "integration and not requires_db"

Практический workflow (реальный проект)

pytest.ini с полным набором маркеров

[pytest]
markers =
    # По скорости
    slow: Медленные тесты (> 5 секунд)
    fast: Быстрые тесты (< 1 секунды)
 
    # По типу
    unit: Unit-тесты (изолированные)
    integration: Integration-тесты с внешними системами
    e2e: End-to-end тесты (полный цикл)
 
    # По зависимостям
    requires_db: Требует PostgreSQL
    requires_redis: Требует Redis
    requires_api: Требует внешний API
 
    # По функциональности
    auth: Тесты аутентификации
    payments: Тесты платежей
    api: Тесты API endpoints

Типичные команды

# Разработка: быстрые unit-тесты
pytest -m "unit and not slow"
 
# Перед commit: unit + integration (без slow)
pytest -m "not slow and not e2e"
 
# CI: все тесты
pytest
 
# Дебаг конкретной фичи: только auth
pytest -m auth
 
# Pre-deploy: только критичные
pytest -m "payments or auth"

Пример реального теста

import pytest
 
@pytest.mark.integration
@pytest.mark.slow
@pytest.mark.requires_db
@pytest.mark.payments
def test_stripe_payment_with_refund():
    """
    Полный цикл платежа:
    1. Создание charge в Stripe
    2. Сохранение в БД
    3. Рефанд
    4. Проверка статуса в БД
    """
    # Arrange
    payment = create_payment(amount=100, currency="usd")
 
    # Act
    charge = process_stripe_charge(payment)
    save_to_db(charge)
    refund = refund_payment(charge.id)
 
    # Assert
    assert refund.status == "succeeded"
    assert Payment.get(charge.id).status == "refunded"

Запустить только этот тест:

pytest -m "payments and integration"

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

  • @pytest.mark.slow — маркировка медленных тестов
  • pytest -m — выборочный запуск
  • pytest.ini markers — регистрация маркеров
  • Логические операторыand, or, not
  • Группировка тестов — по скорости, типу, функциональности
  • Real-world workflow — разные команды для dev/CI

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

Поздравляю! Вы изучили 6 из 8 основных уроков pytest-basics!

В следующем уроке вы узнаете про фикстуры — один из самых мощных инструментов pytest. Больше не придётся копировать setup-код в каждом тесте!

Переходите к уроку 6: Fixtures: создаём данные один раз

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

  • @pytest.fixture для переиспользования setup
  • Фикстуры с yield для teardown
  • Scope фикстур (function, class, module)
  • Как избавиться от дублирования в тестах

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

Добавьте маркер в pytest.ini в секцию [pytest]

Проверьте:

  • Откройте pytest.ini и перечислите маркеры в блоке markers
  • Сохраните описание для каждого маркера (slow, integration и т.д.)

Проверьте написание и применение маркера

Проверьте:

  • Используйте нижний регистр: @pytest.mark.slow
  • Декоратор стоит над тестом
  • Запускаете pytest из корня, чтобы pytest.ini подхватился

Используйте отрицание: pytest -m "not slow"

Проверьте:

  • Убедитесь, что медленные тесты действительно помечены slow
  • Для сложных условий комбинируйте not/and/or

Комбинируйте маркеры логическим AND

Проверьте:

  • Пример: pytest -m "integration and payments"
  • Убедитесь, что тест помечен обоими маркерами
Маркеры: запускаем нужные тесты — Pytest с нуля: Первые тесты за 2.5 часа — Potapov.me