Ответ
В pytest параметр scope у декоратора @pytest.fixture определяет жизненный цикл фикстуры — как часто она будет создаваться и уничтожаться.
Существуют следующие уровни scope (от самого узкого к самому широкому):
function: (По умолчанию) Фикстура выполняется для каждой тестовой функции. Обеспечивает максимальную изоляцию тестов.class: Фикстура выполняется один раз на тестовый класс.module: Фикстура выполняется один раз на модуль (файл).package: Фикстура выполняется один раз на пакет (директорию с__init__.py).session: Фикстура выполняется один раз на всю тестовую сессию.
Как выбрать правильный scope?
Выбор зависит от ресурсоемкости инициализации и необходимости изоляции.
- Используйте
functionдля полной изоляции, когда каждый тест должен работать с "чистым" состоянием. - Используйте
moduleилиsessionдля "тяжелых" ресурсов (например, подключение к БД, запуск Docker-контейнера), инициализация которых занимает много времени. Это значительно ускоряет выполнение тестов.
Пример (scope='module'):
import pytest
# Эта фикстура будет вызвана только один раз для всего файла
@pytest.fixture(scope='module')
def db_connection():
print("nУстановка соединения с БД...")
conn = create_db_connection() # Условная функция
yield conn
print("nЗакрытие соединения с БД...")
conn.close()
# Оба теста будут использовать один и тот же экземпляр db_connection
def test_query1(db_connection):
assert db_connection.execute("SELECT 1") is not None
def test_query2(db_connection):
assert db_connection.execute("SELECT 2") is not None
Важно: фикстуры с широким scope (module, session) должны быть потокобезопасными при параллельном запуске тестов (например, с pytest-xdist).
Ответ 18+ 🔞
Давай разберём эту тему про scope в pytest, а то некоторые думают, что это про снайперские винтовки, а не про тесты. Суть в том, как часто твоя фикстура будет выёбываться и создаваться заново.
Вот какие есть уровни, от самого назойливого до самого ленивого:
function(стоит по умолчанию): Фикстура отрабатывает для каждого отдельного теста. Каждый тест — как будто первый день в армии, всё с чистого листа. Максимальная изоляция, но иногда овердохуища лишней работы.class: Фикстура прикидывается умной и создаётся один раз на весь класс. Экономия, блядь, на спичках, но иногда работает.module: Вообще красота! Фикстура выполняется один раз на файл. Запустилась в начале файла и до конца сессии терпит все твои тесты.package: Для тех, кто любит порядок. Один раз на папку (пакет). Редко кто заморачивается, но жить можно.session: Апогей лени! Фикстура создаётся один раз за весь прогон и потом живёт, пока все тесты не отстреляются. Идеально для чего-то тяжёлого, вроде поднятия целой инфраструктуры.
Так какую же выбрать, ёпта?
Всё просто, как три копейки:
function— когда тестам надо быть изолированными, чтобы они друг другу состояния не портили. Дефолтный и часто правильный выбор.module/session— когда инициализация фикстуры — это такой геморрой, что проще один раз потратиться, а потом всем раздавать. Например, подключение к базе или запуск сервиса. Скорость тестов взлетает просто в небеса.
Смотри, как это выглядит на практике (scope='module'):
import pytest
# Эта штука отработает один раз на файл, а не на каждый тест
@pytest.fixture(scope='module')
def db_connection():
print("nУстановка соединения с БД...") # Увидишь это сообщение ОДИН раз
conn = create_db_connection() # Условная функция
yield conn # А вот тут она отдаёт соединение всем тестам по очереди
print("nЗакрытие соединения с БД...") # И закрывается тоже один раз в конце
conn.close()
# Оба теста будут пить из одной и той же кружки db_connection
def test_query1(db_connection):
assert db_connection.execute("SELECT 1") is not None
def test_query2(db_connection):
assert db_connection.execute("SELECT 2") is not None
И главное предупреждение, чтоб потом не орать "почему не работает!": если используешь широкие scope (module, session) и гоняешь тесты параллельно (например, через pytest-xdist), убедись, что твоя фикстура не начнёт сама с собой конфликтовать в разных потоках. А то будет весело, в рот меня чих-пых.