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

Как Scope влияет на выполнение фикстуры?

2.0 Middle🔥 231 комментариев
#Python#Фреймворки тестирования

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Влияние Scope на выполнение фикстуры в Pytest

Scope (область видимости) — это один из ключевых параметров фикстуры в Pytest, который определяет жизненный цикл и частоту выполнения кода фикстуры. Он напрямую влияет на производительность тестов, изоляцию данных и управление ресурсами.

Основные значения Scope и их влияние

Pytest поддерживает пять значений scope, от самого узкого до самого широкого:

  1. function (по умолчанию): Фикстура выполняется один раз для каждой тестовой функции.
    *   **Влияние**: Максимальная изоляция тестов. Каждый тест получает новый, чистый объект. Это безопасно, но может быть ресурсоемко (например, при создании соединения с БД для каждого теста).
```python
import pytest

@pytest.fixture(scope='function')
def fresh_user():
    print("\nСоздаем нового пользователя")
    return {"id": 1, "name": "Alice"}

def test_user_name(fresh_user):
    assert fresh_user["name"] == "Alice"

def test_user_id(fresh_user):  # Будет создан ВТОРОЙ пользователь
    assert fresh_user["id"] == 1
```

2. class: Фикстура выполняется один раз для всего тестового класса.

    *   **Влияние**: Все тестовые методы (`def test_...`) внутри одного класса используют один и тот же экземпляр фикстуры. Это эффективно, если методы работают с неизменяемыми данными или если нужно разделить дорогостоящую настройку между ними.
```python
@pytest.fixture(scope='class')
def shared_connection():
    print("\nУстанавливаем соединение с БД")
    conn = {"status": "connected"}
    yield conn
    print("\nЗакрываем соединение с БД")

class TestDatabase:
    def test_insert(self, shared_connection):
        assert shared_connection["status"] == "connected"
        shared_connection["last_action"] = "insert"  # Изменение состояния!

    def test_select(self, shared_connection):
        # Используется то же соединение, что и в test_insert
        assert shared_connection.get("last_action") == "insert"
```

3. module: Фикстура выполняется один раз для модуля (файла с тестами).

    *   **Влияние**: Все тестовые функции и классы в одном файле `.py` используют один экземпляр. Идеально для кэширования данных или настройки глобального состояния модуля (например, чтение конфигурационного файла).

  1. package: Фикстура выполняется один раз для пакета (директории с __init__.py).
    *   **Влияние**: Редко используется, но позволяет разделить ресурсы между всеми тестовыми модулями внутри пакета.

  1. session: Фикстура выполняется один раз за всю сессию тестирования (за один запуск pytest).
    *   **Влияние**: Максимальная эффективность. Объект создается один раз и используется во всех тестах. Критически важен для управления тяжелыми ресурсами: **база данных**, **веб-драйвер Selenium**, **API-клиент**. Однако требует осторожности с состоянием.
```python
@pytest.fixture(scope='session')
def browser():
    from selenium import webdriver
    print("\nЗапускаем браузер (один раз за сессию)")
    driver = webdriver.Chrome()
    driver.implicitly_wait(10)
    yield driver
    print("\nЗакрываем браузер")
    driver.quit()

def test_login(browser):
    browser.get("https://example.com/login")
    # ... тест логина

def test_profile(browser):  # Используется тот же экземпляр браузера
    browser.get("https://example.com/profile")
    # ... тест профиля
```

Ключевые аспекты влияния Scope

  • Производительность vs. Изоляция: Чем шире scope (например, session), тем меньше накладных расходов на создание/уничтожение ресурсов, что ускоряет прогон тестов. Чем уже scope (function), тем выше изоляция тестов, что делает их более стабильными и независимыми.
  • Состояние (State): Фикстуры с широким scope (кроме function) становятся разделяемым состоянием. Изменение данных фикстуры в одном тесте повлияет на все последующие тесты, использующие эту фикстуру. Это может быть как полезным (кеширование), так и источником трудноуловимых багов.
  • Финализаторы (yield / addfinalizer): Момент выполнения "очистки" (кода после yield) также зависит от scope. Для function очистка выполняется после каждого теста, для session — только после выполнения всех тестов в сессии.
  • Динамическое поведение: Фикстура с более широким scope не может зависеть от фикстуры с более узким scope. Например, фикстура session не может запросить фикстуру function, так как ее жизненный цикл короче. Это логичное ограничение Pytest.

Практические рекомендации

  • Начинайте с scope='function' по умолчанию для безопасности.
  • Повышайте scope до class/module/session осознанно, когда:
    *   Создание ресурса очень медленное (веб-драйвер, подключение к БД).
    *   Ресурс является stateless или его состояние легко сбросить между тестами.
    *   Тесты только читают данные из фикстуры, не изменяя их.
  • Для фикстур с широким scope:
    *   Избегайте хранения изменяемого состояния.
    *   Используйте **откат транзакций** (для БД) или **очистку cookies/сессий** (для браузера) в каждом тесте, чтобы вернуть общее состояние в исходную точку.
    *   Рассмотрите возможность использования `autouse=True` для автоматической инициализации глобальных ресурсов.

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