← Назад к вопросам

Как группировать тесты в pytest?

2.0 Middle🔥 161 комментариев
#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Группировка тестов в pytest

Организация тестов — критична для поддержки больших test suite'ов. pytest предоставляет несколько мощных инструментов для группировки, фильтрации и запуска тестов.

1. Группировка по классам (pytest-классы)

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

class TestUserAuthentication:
    """Группа тестов для аутентификации"""
    
    def test_login_with_valid_credentials(self):
        user = authenticate("john@example.com", "password123")
        assert user.is_authenticated
    
    def test_login_with_invalid_credentials(self):
        with pytest.raises(AuthenticationError):
            authenticate("john@example.com", "wrongpassword")
    
    def test_login_with_nonexistent_user(self):
        with pytest.raises(UserNotFoundError):
            authenticate("ghost@example.com", "password")

class TestUserProfile:
    """Группа тестов для профиля пользователя"""
    
    def test_update_profile_name(self):
        user = User(name="Old Name")
        user.update(name="New Name")
        assert user.name == "New Name"
    
    def test_profile_cannot_be_empty(self):
        user = User()
        with pytest.raises(ValidationError):
            user.validate()

Преимущества:

  • Логическая организация — тесты для одного функционала вместе
  • Параллельный запуск — pytest может распределять классы между воркерами
  • Фиксчуры уровня класса — можно использовать setup_method и teardown_method

2. Маркеры (marks) для категоризации

Маркеры — это tags, которые помогают фильтровать тесты при запуске.

import pytest

@pytest.mark.unit
def test_simple_calculation():
    assert 2 + 2 == 4

@pytest.mark.integration
def test_database_connection():
    db = connect_to_database()
    assert db.is_alive()

@pytest.mark.slow
@pytest.mark.integration
def test_heavy_processing():
    result = process_large_dataset()
    assert len(result) > 0

@pytest.mark.skip(reason="Feature not implemented yet")
def test_future_feature():
    pass

@pytest.mark.xfail(reason="Known bug in production")
def test_known_bug():
    assert 1 == 2  # Ожидаемо падает

Запуск с маркерами:

# Запустить только unit-тесты
pytest -m unit

# Запустить integration, но исключить slow
pytest -m "integration and not slow"

# Запустить всё, кроме медленных
pytest -m "not slow"

Определение собственных маркеров (pytest.ini или pyproject.toml):

[pytest]
markers =
    unit: Unit tests
    integration: Integration tests
    slow: Slow tests
    smoke: Smoke tests
    api: API tests

3. Структура папок

Пропер файловая структура делает поиск тестов интуитивным:

tests/
├── unit/
│   ├── domain/
│   │   ├── test_user.py
│   │   └── test_order.py
│   ├── application/
│   │   ├── test_user_service.py
│   │   └── test_order_service.py
│   └── infrastructure/
│       └── test_database.py
├── integration/
│   ├── test_api.py
│   ├── test_database.py
│   └── test_cache.py
├── e2e/
│   └── test_user_flow.py
└── conftest.py

запуск только unit-тестов:

pytest tests/unit/
pytest tests/unit/domain/
pytest tests/unit/domain/test_user.py::TestUserCreation

4. Параметризация для множественных случаев

Параметризация позволяет запустить один тест с разными входными данными.

import pytest

@pytest.mark.parametrize("input,expected", [
    (2, 4),
    (3, 9),
    (5, 25),
])
def test_square(input, expected):
    assert input ** 2 == expected

# Параметризация с несколькими параметрами
@pytest.mark.parametrize("user_type,can_delete", [
    ("admin", True),
    ("moderator", True),
    ("user", False),
    ("guest", False),
])
def test_delete_permission(user_type, can_delete):
    user = create_user(user_type)
    assert user.can_delete_posts() == can_delete

Результаты в консоли:

test_square[2-4] PASSED
test_square[3-9] PASSED
test_square[5-25] PASSED

5. Группировка с конфигурацией (conftest.py)

Фиксчуры позволяют группировать setup/teardown логику и переиспользовать её:

# tests/conftest.py
import pytest
from app.database import Database

@pytest.fixture(scope="session")
def db_session():
    """Фиксчура уровня сессии — создаётся один раз"""
    db = Database(":memory:")
    db.create_tables()
    yield db
    db.close()

@pytest.fixture(scope="function")
def db_transaction(db_session):
    """Фиксчура уровня теста — каждый тест получит транзакцию"""
    transaction = db_session.begin_transaction()
    yield transaction
    transaction.rollback()

@pytest.fixture
def user(db_transaction):
    """Фиксчура-зависимость"""
    user = User(name="John", email="john@example.com")
    db_transaction.add(user)
    return user

# tests/unit/test_user_service.py
from conftest import user

class TestUserService:
    def test_get_user_by_email(self, user, db_transaction):
        found = db_transaction.query(User).filter_by(email=user.email).first()
        assert found.name == "John"

6. Скипание и условное выполнение

import pytest
import sys

@pytest.mark.skipif(sys.version_info < (3, 9), reason="Requires Python 3.9+")
def test_new_feature():
    pass

@pytest.mark.skipif(not has_postgres(), reason="PostgreSQL not available")
def test_database():
    pass

# Условный skip при запуске
def test_optional(request):
    if "slow" not in request.config.getoption("-m"):
        pytest.skip("Running without slow tests")

7. Запуск конкретных групп

# Запустить тесты по имени
pytest tests/ -k "test_login"
pytest tests/ -k "Authentication"

# Запустить по маркерам
pytest -m "unit or smoke"

# Запустить с verbose выводом группировок
pytest tests/ -v --tb=short

# Параллельный запуск (требует pytest-xdist)
pytest -n auto

Ключевые моменты

  • Классы для логических групп — аутентификация, профиль, платежи
  • Маркеры для быстрого фильтра — unit/integration/slow
  • Параметризация для множественных сценариев — разные входные данные
  • Фиксчуры для setup/teardown — переиспользуемая логика инициализации
  • Структура папок отражает архитектуру — unit/integration/e2e

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

Как группировать тесты в pytest? | PrepBro