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

В практической ситуации тест находится во вложенном каталоге, где одноимённые фикстуры объявлены в двух con...

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

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

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

pytest выберет фикстуру из ближайшего к тесту conftest.py. Фикстура из более близкого каталога перекрывает одноимённую фикстуру из родительского каталога.

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

Система fixtures в pytest появилась как способ отделить подготовку окружения теста от его логики и сделать эту подготовку повторно используемой. Размещение общих фикстур в conftest.py позволяет автоматически предоставлять их тестам без явных импортов.

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

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

Например, в tests/conftest.py определена фикстура подключения к общей тестовой базе, а в tests/api/conftest.py — фикстура с тем же именем для изолированной базы API. Тест из tests/api не получит обе фикстуры и не выберет вариант случайно.

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

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

При разрешении фикстуры pytest рассматривает пространство имён от теста наружу: сначала локальные определения тестового модуля, затем conftest.py в текущем каталоге, затем файлы в родительских каталогах. Используется первое найденное определение с нужным именем.

Минимальный пример:

# tests/conftest.py import pytest @pytest.fixture def client(): return "общий клиент"
# tests/api/conftest.py import pytest @pytest.fixture def client(): return "клиент API"

Тест из tests/api получит значение "клиент API". pytest не объединяет тела двух одноимённых фикстур и не вызывает родительскую автоматически.

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

Важное ограничение: поиск зависит от положения теста в дереве коллекции, а не от того, где физически импортирована функция теста. Перемещение теста в другой каталог может изменить используемую фикстуру без изменения самого тестового кода.

Переопределение удобно для специализации, но опасно при совпадении имён случайно. Для разных ресурсов лучше использовать разные имена, а намеренное переопределение фиксировать в документации или небольшим тестом конфигурации.

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

В проекте общая фикстура database создавала PostgreSQL-схему для интеграционных тестов. Для тестов платежного API потребовалась база с дополнительными таблицами, поэтому в tests/payments/conftest.py появилась фикстура с тем же именем.

Рассматривались три варианта. Переименовать фикстуру в payments_database было наиболее явно, но потребовало изменить множество тестов. Импортировать нужную фикстуру вручную было хуже: это обходило стандартную модель видимости pytest и усложняло управление зависимостями. Намеренно переопределить database в дочернем conftest.py было компактно, но создавало риск неправильных ожиданий у новых разработчиков.

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

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

  1. Объединяет ли pytest одноимённые фикстуры из нескольких conftest.py?

Нет. Выбирается одно определение — ближайшее по области видимости. Тело родительской фикстуры не выполняется автоматически и не становится частью дочерней.

Если нужно использовать общую подготовку и добавить специализацию, безопаснее вынести общую часть в фикстуру с другим именем, а локальную сделать зависимой от неё. Иначе получить «родительскую» реализацию по тому же имени напрямую нельзя.

  1. Изменится ли фикстура, если переместить тест в соседний каталог?

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

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

  1. Может ли фикстура из плагина перекрыть локальную фикстуру?

Локальная фикстура обычно имеет приоритет над фикстурой, предоставленной плагином: pytest сначала ищет определения в тестовом модуле и каталогах проекта, а затем использует внешние источники.

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