Программирование PythonТестированиеРазработчик Python, отвечающий за тестовую инфраструктуру

Зачем pytest рассматривает conftest.py как локальный плагин, а не как обычный модуль тестового проекта?

Зачем pytest рассматривает conftest.py как локальный плагин, а не как обычный модуль тестового проекта?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

conftest.py — это локальный плагин pytest: pytest автоматически обнаруживает его при сборке тестов и делает объявленные там фикстуры доступными тестам в соответствующей директории и её подкаталогах. Такой файл не следует использовать как обычный модуль для импорта общих функций или фикстур.

Исторический контекст

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

Таким образом, pytest отделяет механизм обнаружения тестовой инфраструктуры от обычной архитектуры импортируемых Python-модулей. conftest.py выполняет роль точки расширения pytest, а не публичной библиотеки проекта.

Постановка проблемы

Если импортировать фикстуру напрямую из conftest.py, тест начинает зависеть от способа, которым pytest загрузил этот файл. В разных структурах проекта один и тот же файл может иметь разные пути импорта, что повышает риск дублированной загрузки, циклических импортов, конфликтов имён и ошибок при сборке.

Кроме того, прямой импорт скрывает область действия фикстуры. Читателю теста сложнее понять, откуда она доступна и какое локальное переопределение применит pytest.

Подробное решение

Во время сбора тестов pytest загружает conftest.py как плагин, связанный с конкретным каталогом. Фикстуры из него видимы тестам этого каталога и вложенных каталогов, если более близкий conftest.py не переопределяет ту же фикстуру.

Для повторно используемого кода следует разделять роли:

  • фикстуры, хуки и настройки pytest размещать в conftest.py;
  • фабрики, генераторы данных и обычные вспомогательные функции размещать в обычном импортируемом пакете, например в каталоге тестовых утилит;
  • общую инфраструктуру между несколькими проектами оформлять как pytest-плагин.

Технически импорт conftest.py иногда возможен, но это не делает такой импорт надёжным контрактом. Главный компромисс — небольшое дублирование объявления фикстуры предпочтительнее неявной зависимости от внутреннего способа загрузки pytest.

Ситуация из практики

В проекте тесты из разных каталогов начали импортировать фабрику из conftest.py. Локально всё работало, но при запуске полного набора pytest обнаружил конфликт модулей: часть тестов ссылалась на объект из одной загрузки файла, а часть — на объект из другой.

Можно было оставить прямые импорты и исправлять пути, но это сохраняло хрупкую зависимость от структуры запуска. Другой вариант — перенести весь conftest.py в общий пакет, однако тогда локальные фикстуры потеряли бы естественную область видимости.

Выбранное решение: фабрику вынесли в обычный импортируемый модуль тестовых утилит, а фикстуры оставили в соответствующих conftest.py. После этого pytest самостоятельно управлял фикстурами, а обычный Python-код импортировался единообразно при любом способе запуска.

Что кандидаты часто упускают

  1. Дополнительный вопрос: Видят ли тесты фикстуры из conftest.py, расположенного в соседнем каталоге?

Ответ: Нет, обычное дерево видимости pytest направлено от каталога теста к его родительским каталогам, а не к соседним веткам. Если фикстура нужна нескольким независимым каталогам, её следует разместить в общем родительском conftest.py, подключаемом плагине или явно импортируемом модуле с фабрикой.

  1. Дополнительный вопрос: Что произойдёт, если в дочернем каталоге объявить фикстуру с тем же именем?

Ответ: Для тестов дочернего каталога будет выбрана более близкая фикстура. Это позволяет локально переопределять поведение, но создаёт риск неочевидного изменения сценариев, поэтому такие переопределения стоит делать явно и документировать.

  1. Дополнительный вопрос: Можно ли вынести фикстуру из conftest.py в обычный модуль и импортировать её в тест?

Ответ: Да, но тогда pytest не получит её автоматически только из-за наличия функции в модуле. Фикстуру нужно явно подключить или импортировать способом, поддерживаемым выбранной организацией проекта; при этом теряется часть локальной модели видимости conftest.py. Такой вариант оправдан, когда фикстура действительно является общей библиотечной частью тестовой инфраструктуры.