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

Что такое скоуп у фикстуры?

1.6 Junior🔥 241 комментариев
#Брокеры сообщений#Тестирование

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

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

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

Скоуп у фикстуры в pytest

Скоуп (scope) фикстуры — это время жизни объекта, который создаёт фикстура. Он определяет, когда фикстура инициализируется и когда очищается (teardown).

Пять уровней скоупа

1. function (По умолчанию)

Фикстура создаётся заново для каждого теста.

import pytest

@pytest.fixture  # scope='function' по умолчанию
def user():
    print("Creating user")
    user = {"id": 1, "name": "John"}
    yield user
    print("Cleaning up user")

def test_user_first(user):
    assert user["name"] == "John"
    # Creating user
    # Cleaning up user

def test_user_second(user):
    assert user["id"] == 1
    # Creating user (новый объект!)
    # Cleaning up user

# Вывод:
# Creating user
# Cleaning up user
# Creating user
# Cleaning up user

Когда использовать: При тестировании функций, которые модифицируют объект.

2. class

Фикстура создаётся один раз на класс тестов.

@pytest.fixture(scope='class')
def database():
    print("Connecting to DB")
    db = connect_to_db()
    yield db
    print("Closing DB")

class TestUsers:
    def test_create_user(self, database):
        database.insert("INSERT INTO users...")
        # Connecting to DB
    
    def test_get_user(self, database):
        result = database.query("SELECT * FROM users")
        # Используется ОДНО и то же подключение
    
    def test_delete_user(self, database):
        database.delete("DELETE FROM users...")
    # Closing DB (после всех тестов класса)

# Вывод:
# Connecting to DB
# Closing DB

Когда использовать: При работе с дорогостоящим ресурсом (БД, API).

3. module

Фикстура создаётся один раз на файл тестов.

# test_auth.py
@pytest.fixture(scope='module')
def app():
    print("Starting Flask app")
    app = create_app()
    app.config['TESTING'] = True
    yield app
    print("Shutting down app")

def test_login(app):
    with app.test_client() as client:
        response = client.post('/login', json={"user": "admin"})
        # Starting Flask app

def test_logout(app):
    with app.test_client() as client:
        response = client.post('/logout')
        # Используется ТОТ ЖЕ экземпляр app
    # Shutting down app (после всех тестов в файле)

# Вывод:
# Starting Flask app
# Shutting down app

Когда использовать: При инициализации тестового окружения один раз.

4. session

Фикстура создаётся один раз на сессию тестирования (весь тестовый прогон).

@pytest.fixture(scope='session')
def redis_client():
    print("Connecting to Redis")
    client = redis.Redis()
    yield client
    print("Closing Redis")

# Если запустить `pytest test_*.py`
# Redis подключится один раз для ВСЕХ тестов
# test_file1.py::test_1 (использует redis_client)
# test_file1.py::test_2 (используют ОДНО подключение)
# test_file2.py::test_3 (используют ОДНО подключение)
# ... затем очистка

Когда использовать: Для инициализации единого ресурса для всех тестов.

5. package

Фикстура создаётся один раз на пакет.

# conftest.py в директории tests/
@pytest.fixture(scope='package')
def config():
    print("Loading config")
    config = load_config()
    yield config
    print("Cleaning config")

# tests/unit/test_models.py
def test_model(config):
    assert config['db_host'] == 'localhost'

# tests/integration/test_api.py
def test_api(config):
    assert config['api_key'] is not None

# Один раз для package tests/, затем для package tests/unit/, затем tests/integration/

Практический пример: БД с разными скоупами

import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# Скоуп session: одно подключение для всех тестов
@pytest.fixture(scope='session')
def db_engine():
    print("Creating DB engine")
    engine = create_engine('sqlite:///:memory:')
    yield engine
    print("Disposing engine")

# Скоуп function: новая сессия для каждого теста
@pytest.fixture(scope='function')
def db_session(db_engine):
    print("Creating session")
    connection = db_engine.connect()
    transaction = connection.begin()
    Session = sessionmaker(bind=connection)
    session = Session()
    
    yield session
    
    session.close()
    transaction.rollback()  # Откатываем после теста
    connection.close()
    print("Closing session")

class TestUserRepository:
    def test_create_user(self, db_session):
        # Creating session (новая транзакция)
        user = User(name="John")
        db_session.add(user)
        db_session.commit()
        assert user.id is not None
        # Closing session (откатываем)
    
    def test_get_user(self, db_session):
        # Creating session (новая транзакция)
        # Closing session

# Вывод:
# Creating DB engine
# Creating session
# Closing session (rollback)
# Creating session
# Closing session (rollback)
# Disposing engine

Зависимости между фикстурами

@pytest.fixture(scope='session')
def app():
    return Flask(__name__)

@pytest.fixture(scope='function')
def client(app):  # Зависит от app
    return app.test_client()

def test_request(client):  # Используем client
    response = client.get('/')
    assert response.status_code == 200

# app создаётся один раз
# client создаётся для каждого теста

Автоиспользование фикстур

@pytest.fixture(scope='function', autouse=True)
def clear_cache():
    """Очищает кэш перед каждым тестом автоматически"""
    print("Clearing cache")
    cache.clear()
    yield
    print("Cache cleared")

def test_any():
    # clear_cache вызовется АВТОМАТИЧЕСКИ
    pass

Таблица скоупов

СкоупЧастота созданияИспользование
functionДля каждого тестаИзолированные тесты
classОдин раз на классГруппы тестов
moduleОдин раз на файлИнициализация
sessionОдин раз на сессиюДорогие ресурсы
packageОдин раз на пакетКонфиг

Важные заметки

  • Меньший скоуп вложен в больший (function → class → module → session)
  • Фикстура с большим скоупом может использовать фикстуру с меньшим скоупом
  • Обратное НЕВОЗМОЖНО (function-фикстура не может использовать session-фикстуру)
  • Скоуп влияет на производительность тестов

Заключение

Правильный выбор скоупа — это баланс между скоростью (большие скоупы быстрее) и изолированностью (маленькие скоупы чище). Лучше начинать с function и увеличивать скоуп, только если нужна оптимизация.

Что такое скоуп у фикстуры? | PrepBro