механизмы импорта pytest и sys.path
/PYTHONPATH
¶
Режимы импорта¶
pytest как фреймворк для тестирования должен импортировать тестовые модули и файлы conftest.py
для выполнения.
Импорт файлов в Python (по крайней мере, до недавнего времени) - нетривиальный процесс, часто требующий изменения sys.path
. Некоторые аспекты процесса импорта можно контролировать с помощью флага командной строки --import-mode
, который может принимать такие значения:
prepend
(по умолчанию): путь к каталогу, содержащему каждый модуль, будет вставлен в началоsys.path
, если его там еще нет, а затем импортирован с помощью встроенного модуля__import__
.Это требует, чтобы имена тестовых модулей были уникальными, если дерево тестовых каталогов не организовано в пакеты, поскольку после импорта модули будут помещаться в
sys.modules
.Это классический механизм, восходящий к тому времени, когда еще поддерживался Python 2.
append
: каталог, содержащий каждый модуль, добавляется в конецsys.path
, если его там еще нет, и импортируется с помощью__import__
.Это позволяет запускать тестовые модули против установленных версий пакета, даже если тестируемый пакет имеет тот же корень импорта. Например:
testing/__init__.py testing/test_pkg_under_test.py pkg_under_test/
тесты будут работать с установленной версией
pkg_under_test
при использовании--import-mode=append
, тогда как при использованииprepend
они будут брать локальную версию. Именно поэтому мы выступаем за использование макетов src.То же, что и
prepend
, требует, чтобы имена тестовых модулей были уникальными, если дерево тестовых каталогов не организовано в пакеты, поскольку после импорта модули будут помещаться вsys.modules
.importlib
: новый в pytest-6.0, этот режим используетimportlib
для импорта тестовых модулей. Это дает полный контроль над процессом импорта и не требует измененияsys.path
.По этой причине здесь не требуется, чтобы имена тестовых модулей были уникальными.
Однако один недостаток заключается в том, что тестовые модули не импортируются друг другом. Кроме того, служебные модули в каталогах tests не импортируются автоматически, поскольку каталог tests больше не добавляется в
sys.path
.Изначально мы планировали сделать
importlib
по умолчанию в будущих релизах, однако теперь ясно, что у него есть свои недостатки, поэтому в обозримом будущем по умолчанию останетсяprepend
.
См.также
Переменная конфигурации pythonpath
.
Сценарии режимов импорта prepend
и append
¶
Вот список сценариев при использовании режимов импорта prepend
или append
, в которых pytest должен изменить sys.path
для импорта тестовых модулей или conftest.py
файлов, и проблемы, с которыми пользователи могут столкнуться из-за этого.
Тестовые модули / conftest.py
файлы внутри пакетов¶
Рассмотрим следующее расположение файлов и каталогов:
root/
|- foo/
|- __init__.py
|- conftest.py
|- bar/
|- __init__.py
|- tests/
|- __init__.py
|- test_foo.py
При выполнении:
pytest root/
pytest найдет foo/bar/tests/test_foo.py
и поймет, что это часть пакета, учитывая, что в той же папке есть файл __init__.py
. Затем он будет искать вверх, пока не найдет последнюю папку, которая все еще содержит файл __init__.py
, чтобы найти корень пакета (в данном случае foo/
). Чтобы загрузить модуль, он вставит root/
перед sys.path
(если его там еще нет), чтобы загрузить test_foo.py
как модуль foo.bar.tests.test_foo
.
Та же логика применима к файлу conftest.py
: он будет импортирован как модуль foo.conftest
.
Сохранение полного имени пакета важно, когда тесты живут в пакете, чтобы избежать проблем и позволить тестовым модулям иметь дублирующиеся имена. Это также подробно обсуждается в Соглашения для обнаружения тестов в Python.
Автономные модули тестирования / файлы conftest.py
¶
Рассмотрим следующее расположение файлов и каталогов:
root/
|- foo/
|- conftest.py
|- bar/
|- tests/
|- test_foo.py
При выполнении:
pytest root/
pytest найдет foo/bar/tests/test_foo.py
и поймет, что он НЕ является частью пакета, учитывая, что в той же папке нет файла __init__.py
. Затем он добавит root/foo/bar/tests
к sys.path
, чтобы импортировать test_foo.py
как модуль test_foo
. То же самое делается с файлом conftest.py
путем добавления root/foo
к sys.path
, чтобы импортировать его как conftest
.
По этой причине в данном макете не может быть тестовых модулей с одинаковым именем, так как все они будут импортированы в глобальное пространство имен импорта.
Это также подробно обсуждается в Соглашения для обнаружения тестов в Python.
Вызов pytest
в отличие от python -m pytest
¶
Запуск pytest с pytest [...]
вместо python -m pytest [...]
приводит к почти эквивалентному поведению, за исключением того, что последний добавит текущий каталог в sys.path
, что является стандартным поведением python
.
См. также Вызов pytest через python -m pytest.