Pytest Scopes и Параметризация

Полное руководство по областям действия фикстур и параметризации в Pytest

1. Pytest Scopes

Scopes определяют, как часто фикстура запускается. Это ключевой механизм управления жизненным циклом тестовых данных.

Scope Описание Когда запускается
functionПо умолчаниюДля каждой тестовой функции
classНа уровне классаОдин раз на класс, независимо от количества методов
moduleНа уровне модуляОдин раз на файл .py
sessionНа уровне сессииОдин раз на всю сессию pytest
2. Scopes с Функциями

scope="function" (по умолчанию)

@pytest.fixture
def database_connection():
    print("\n[SETUP] Создание подключения к БД")
    conn = "DB Connection Object"
    yield conn
    print("\n[TEARDOWN] Закрытие подключения к БД")

def test_user_login(database_connection):
    print(f"\n[Test] Используем: {database_connection}")
    assert True
[SETUP] Создание подключения к БД [Test] Используем: DB Connection Object [PASSED] [TEARDOWN] Закрытие подключения к БД [SETUP] Создание подключения к БД [Test] Используем: DB Connection Object [PASSED] [TEARDOWN] Закрытие подключения к БД

Важно: Фикстура запускается 2 раза — по одному для каждой тестовой функции. Это может быть проблемой, если создание соединения дорогостоящая операция.

scope="class"

@pytest.fixture(scope="class")
def api_client():
    print("\n[SETUP] Создание API клиента")
    client = "API Client"
    yield client
    print("\n[TEARDOWN] Закрытие API клиента")

class TestUserAPI:
    def test_login(self, api_client):
        assert True

    def test_get_profile(self, api_client):
        assert True
[SETUP] Создание API клиента [Test] api_client: API Client [PASSED] [Test] api_client: API Client [PASSED] [Test] api_client: API Client [PASSED] [TEARDOWN] Закрытие API клиента

Важно: Фикстура запускается 1 раз на весь класс. Все три теста используют один и тот же объект api_client.

scope="module"

@pytest.fixture(scope="module")
def test_database():
    print("\n[SETUP MODULE] Инициализация тестовой БД")
    db = "Test Database"
    yield db
    print("\n[TEARDOWN MODULE] Удаление тестовой БД")

Важно: Все тесты в модуле (включая класс) используют один и тот же test_database.

scope="session"

# conftest.py или любой модуль
@pytest.fixture(scope="session")
def browser_driver():
    print("\n[SETUP SESSION] Запуск Browser Driver")
    driver = "Chrome Driver"
    yield driver
    print("\n[TEARDOWN SESSION] Закрытие Browser Driver")

Важно: browser_driver создается один раз и используется во всех тестовых файлах сессии.

3. Автоиспользование с autouse
@pytest.fixture(scope="class", autouse=True)
def setup_database():
    print("\n[SETUP] Подключение к БД")
    db = "Database Connection"
    yield db
    print("\n[TEARDOWN] Отключение от БД")

class TestDatabaseOperations:
    def test_insert(self):
        assert True

Примечание: autouse=True автоматически применяет фикстуру ко всем тестам без явного указания в параметрах.

4. Параметризация

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

Базовый пример @pytest.mark.parametrize

@pytest.mark.parametrize("input,expected", [
    (1, 2),
    (3, 6),
    (5, 10)
])
def test_double(input, expected):
    assert input * 2 == expected
test_double[1-2] PASSED test_double[3-6] PASSED test_double[5-10] PASSED

Важно: Тест запустится 3 раза с разными параметрами.

Параметризация фикстур

@pytest.fixture(params=["Chrome", "Firefox", "Safari"])
def browser(request):
    print(f"\n[SETUP] Запуск {request.param}")
    yield f"{request.param} Driver"
    print(f"\n[TEARDOWN] Закрытие {request.param}")
[SETUP] Запуск Chrome Testing with Chrome Driver [PASSED] [SETUP] Запуск Firefox Testing with Firefox Driver [PASSED] [SETUP] Запуск Safari Testing with Safari Driver [PASSED]

Объединение параметризации и scopes

@pytest.fixture(scope="module", params=["PostgreSQL", "MySQL", "SQLite"])
def database(request):
    yield f"{request.param} Connection"

@pytest.mark.parametrize("user_count", [1, 10, 100])
def test_insert_users(database, user_count):
    assert True

Важно: Фикстура database с scope="module" выполняется один раз для каждого уникального request.param. То есть: 3 базы × 3 количества = 9 тестов.

5. scope + parametrize

scope="function" + parametrize

@pytest.fixture(scope="function")
def api_token():
    yield "ABC123"

@pytest.mark.parametrize("endpoint", ["/users", "/orders", "/products"])
def test_api_endpoints(api_token, endpoint):
    assert True

3 теста × 2 вызова setup/teardown = 6 операций с токеном.

scope="class" + parametrize

@pytest.fixture(scope="class")
def shared_api_client():
    print("\n[SETUP CLASS] Создание API клиента")
    yield "API Client Object"

@pytest.mark.parametrize("endpoint", ["/users", "/orders"])
class TestAPIEndpoints:
    def test_get(self, shared_api_client, endpoint):
        assert True

Важно: API клиент создается один раз на класс, несмотря на 4 запуска тестов. Это оптимизация.

6. Практические паттерны

Fixtures с зависимостями

@pytest.fixture(scope="session")
def config():
    return {"base_url": "https://api.example.com", "timeout": 30}

@pytest.fixture(scope="module")
def api_client(config):
    return f"Client -> {config['base_url']}"

@pytest.fixture(scope="function")
def auth_token(api_client, config):
    return f"Token for {api_client}"

Порядок создания: config (session) → api_client (module) → auth_token (function)

Передача фикстур в фикстуры

@pytest.fixture(scope="function")
def user_repository(db_connection):
    return f"UserRepository({db_connection})"

@pytest.fixture(scope="function")
def service(user_repository, order_repository):
    return f"Service({user_repository}, {order_repository})"
7. Итоговая таблица
Scope 1 тест 2 теста 3 теста (1 класс) 10 тестов (2 класса)
function1 setup2 setups3 setups10 setups
class1 setup1 setup1 setup2 setups
module1 setup1 setup1 setup1 setup
session1 setup1 setup1 setup1 setup

Параметризация умножает количество запусков: если у вас 3 теста и 4 параметра — это 12 реальных запусков тестов.

8. Рекомендации
F
scope="function"

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

C
scope="class"

Для тяжелых ресурсов, используемых несколькими тестами в одном классе (например, API клиент)

M
scope="module"

Для ресурсов, которые можно переиспользовать во всём модуле (БД, конфиг)

S
scope="session"

Для глобальных ресурсов (браузер, сессия авторизации)

P
Параметризация + scope

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