Когда pytest создаёт package-scoped фикстуру и когда освобождает её?
Package-scoped фикстура создаётся один раз для тестового пакета при первом тесте, который от неё зависит, и освобождается после завершения последнего такого теста в этом пакете. Поэтому несколько тестовых модулей одного пакета используют один экземпляр фикстуры, если не возникает отдельный экземпляр в другой области пакета.
Фикстуры pytest поддерживают разные области жизни, чтобы отделить настройку теста от управления ресурсами. Узкая область уменьшает влияние тестов друг на друга, а широкая позволяет не повторять дорогую подготовку для каждого теста или модуля.
Package scope нужен как промежуточный вариант между областью модуля и всей тестовой сессией: ресурс можно разделить между тестами одного пакета, не делая его глобальным для всего запуска.
Предположим, пакет содержит несколько тестовых модулей, которым требуется один подготовленный ресурс: схема базы данных, контейнер или тестовая конфигурация. Если использовать function-scoped фикстуру, ресурс будет создаваться слишком часто; если session-scoped — разные пакеты могут неожиданно влиять друг на друга.
Неверное понимание границы package scope приводит либо к лишним затратам, либо к ошибкам изоляции. Особенно важно помнить, что экземпляр фикстуры создаётся не просто при импорте файла, а по требованию первого зависимого теста.
Pytest строит зависимости теста и определяет область действия каждой фикстуры. Для package-scoped фикстуры первый тест в соответствующем пакете вызывает её подготовку; результат затем переиспользуется всеми зависимыми тестами в пределах этой области.
После завершения последнего теста пакета pytest запускает финализацию фикстуры. Для фикстуры с yield код после yield выполняется именно на этом этапе; для addfinalizer вызывается зарегистрированный финализатор.
Порядок создания подчиняется зависимостям: сначала подготавливаются фикстуры, от которых зависит package-scoped фикстура, затем она сама. При разрушении используется обратный порядок. Более широкая область не может зависеть от более узкой: например, package-scoped фикстура не должна зависеть от function-scoped фикстуры, потому что один долгоживущий объект не может корректно использовать множество краткоживущих экземпляров.
Важно различать пакет тестов и произвольную папку. Граница package scope определяется структурой тестового пакета pytest, поэтому расположение conftest.py, наличие пакетной структуры и область, в которой фикстура объявлена, влияют на доступность и экземпляр. Нельзя полагаться только на визуальную вложенность каталогов.
Минимальный пример:
Тесты внутри пакета shop, которые запрашивают catalog, получат один общий объект в рамках package scope. Очистка выполнится после завершения использования фикстуры, а не после каждого теста.
В проекте каждый тестовый модуль создавал локальную тестовую схему базы данных. Это давало хорошую изоляцию, но запуск занимал несколько минут, поскольку схема была одинаковой для всех тестов пакета.
Рассматривались два варианта. Session scope был быстрее, но создавал общий ресурс для всех пакетов и усложнял параллельный запуск. Module scope сохранял изоляцию между файлами, однако повторял дорогую инициализацию для каждого модуля.
Выбрали package scope: схема создавалась один раз для пакета, а тесты внутри него использовали общий подготовленный ресурс. Тесты, которым требовалось изменять состояние, дополнительно очищали свои записи на уровне функции. Это сохранило приемлемую изоляцию данных и сократило время запуска без чрезмерно глобального состояния.
Нет. Обычная фикстура pytest создаётся лениво — только когда она входит в зависимости выполняемого теста, другой фикстуры или подключена как автоматически используемая. Само наличие объявления в conftest.py не запускает её подготовку.
Нет, package scope не равен session scope. Тесты разных пакетов получают разные экземпляры, если фикстура применяется в каждом из них. Это и есть основной компромисс: ресурс шире модуля, но уже всей тестовой сессии.
Только если тесты явно восстанавливают состояние или фикстура предоставляет изолированные операции. Сам pytest не копирует возвращённый объект между тестами: все они видят один экземпляр. Поэтому изменяемое состояние может вызвать порядок-зависимые тесты; для строгой изоляции лучше выбрать более узкую область или добавить очистку на уровне функции.