Передовая практика интеграции¶
Установите пакет с помощью pip¶
Для разработки мы рекомендуем вам использовать venv
для виртуальных сред и pip для установки вашего приложения и любых зависимостей, а также сам пакет pytest
. Это гарантирует, что ваш код и зависимости будут изолированы от системной установки Python.
Создайте файл pyproject.toml
в корне вашего репозитория, как описано в Packaging Python Projects. Первые несколько строк должны выглядеть следующим образом:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "PACKAGENAME"
где PACKAGENAME
- имя вашего пакета.
Затем вы можете установить свой пакет в «редактируемом» режиме, запустив его из того же каталога:
pip install -e .
что позволяет изменять исходный код (как тестов, так и приложения) и перезапускать тесты по своему усмотрению.
Соглашения для обнаружения тестов в Python¶
pytest
реализует следующее стандартное обнаружение теста:
Если аргументы не указаны, то сбор начинается с
testpaths
(если настроено) или с текущего каталога. В качестве альтернативы можно использовать аргументы командной строки в любой комбинации каталогов, имен файлов или идентификаторов узлов.Выполнить перебор каталогов, если они не совпадают с
norecursedirs
.В этих каталогах найдите файлы
test_*.py
или*_test.py
, импортированные по их test package name.Из этих файлов соберите тестовые элементы:
test
префиксные тестовые функции или методы вне классаtest
префиксные тестовые функции или методы внутриTest
префиксных тестовых классов (без метода__init__
)
Примеры настройки тестового обнаружения Изменение стандартного (Python) обнаружения тестов.
Внутри модулей Python pytest
также обнаруживает тесты, используя стандартную технику подклассификации unittest.TestCase.
Выбор макета теста / правила импорта¶
pytest
поддерживает две распространенные схемы расположения тестов:
Тесты вне кода приложения¶
Размещение тестов в дополнительном каталоге вне кода приложения может быть полезно, если у вас много функциональных тестов или по другим причинам вы хотите держать тесты отдельно от кода приложения (часто это хорошая идея):
pyproject.toml
src/
mypkg/
__init__.py
app.py
view.py
tests/
test_app.py
test_view.py
...
Это дает следующие преимущества:
Ваши тесты могут работать с установленной версией после выполнения команды
pip install .
.Ваши тесты могут работать с локальной копией с редактируемой установкой после выполнения
pip install --editable .
.
Для новых проектов мы рекомендуем использовать importlib
import mode (подробное объяснение см. в разделе which-import-mode). Для этого добавьте следующее к вашему pyproject.toml
:
[tool.pytest.ini_options]
addopts = [
"--import-mode=importlib",
]
Обычно, но особенно если вы используете стандартный режим импорта prepend
, настойчиво рекомендуется использовать компоновку src
. Здесь корневой пакет вашего приложения располагается в подкаталоге корня, т.е. src/mypkg/
вместо mypkg
.
Такая планировка предотвращает множество распространенных подводных камней и имеет много преимуществ, которые лучше всего объяснены в этом превосходном blog post от Ionel Cristian Mărieș.
Примечание
Если вы не используете редактируемую установку и используете схему src
, как описано выше, вам необходимо расширить путь поиска файлов модуля в Python, чтобы выполнять тесты непосредственно против локальной копии. Вы можете сделать это специальным образом, установив переменную окружения PYTHONPATH
:
PYTHONPATH=src pytest
или на постоянной основе, используя конфигурационную переменную pythonpath
и добавив следующее к вашему pyproject.toml
:
[tool.pytest.ini_options]
pythonpath = "src"
Примечание
Если вы не используете редактируемую установку и не применяете схему src
(mypkg
непосредственно в корневом каталоге), вы можете положиться на тот факт, что Python по умолчанию помещает текущий каталог в sys.path
для импорта вашего пакета и запускает python -m pytest
для выполнения тестов против локальной копии напрямую.
См. раздел Вызов pytest в отличие от python -m pytest для получения дополнительной информации о разнице между вызовом pytest
и python -m pytest
.
Тесты как часть кода приложения¶
Включение каталогов тестов в пакет приложения полезно, если у вас есть прямая связь между тестами и модулями приложения и вы хотите распространять их вместе с приложением:
pyproject.toml
[src/]mypkg/
__init__.py
app.py
view.py
tests/
__init__.py
test_app.py
test_view.py
...
В этой схеме легко запускать тесты, используя опцию --pyargs
:
pytest --pyargs mypkg
pytest
обнаружит, где установлен mypkg
, и будет собирать тесты оттуда.
Обратите внимание, что эта раскладка также работает в сочетании с раскладкой src
, упомянутой в предыдущем разделе.
Примечание
Вы можете использовать пакеты пространства имен (PEP420) для вашего приложения, но pytest все равно будет выполнять обнаружение test package name на основе наличия __init__.py
файлов. Если вы используете одну из двух рекомендованных выше компоновок файловой системы, но уберете __init__.py
файлы из ваших каталогов, то все должно работать. Из «встроенных тестов», однако, вам придется использовать абсолютный импорт для получения доступа к коду вашего приложения.
Примечание
В режимах импорта prepend
и append
, если pytest находит тестовый файл "a/b/test_module.py"
при рекурсии в файловой системе, он определяет имя импорта следующим образом:
определить
basedir
: это первый «восходящий» (по направлению к корню) каталог, не содержащий__init__.py
. Если, например, оба каталогаa
иb
содержат файл__init__.py
, то родительский каталогa
станет каталогомbasedir
.выполните
sys.path.insert(0, basedir)
, чтобы сделать тестовый модуль импортируемым под полным именем импорта.import a.b.test_module
, где путь определяется путем преобразования разделителей путей/
в символы «.». Это означает, что вы должны следовать соглашению о том, что имена каталогов и файлов отображаются непосредственно на имена импорта.
Причина такой несколько эволюционировавшей техники импорта заключается в том, что в больших проектах несколько тестовых модулей могут импортироваться друг из друга, и поэтому выведение канонического имени импорта помогает избежать неожиданностей, таких как двойной импорт тестового модуля.
С --import-mode=importlib
все менее запутанно, поскольку pytest не нужно изменять sys.path
или sys.modules
, что делает ситуацию гораздо менее удивительной.
Выбор режима импорта¶
По историческим причинам pytest по умолчанию использует режим импорта prepend
import mode вместо importlib
, который мы рекомендуем для новых проектов. Причина кроется в том, как работает режим prepend
:
Поскольку нет пакетов, от которых можно было бы получить полное имя пакета, pytest
будет импортировать ваши тестовые файлы как модули верхнего уровня. Тестовые файлы в первом примере (src layout) будут импортированы как модули верхнего уровня test_app
и test_view
, добавив tests/
к sys.path
.
Это приводит к недостатку по сравнению с режимом импорта importlib
: ваши тестовые файлы должны иметь уникальные имена.
Если вам необходимо иметь тестовые модули с одинаковыми именами, в качестве обходного пути вы можете добавить файлы __init__.py
в папку tests
и подпапки, изменив их на пакеты:
pyproject.toml
mypkg/
...
tests/
__init__.py
foo/
__init__.py
test_view.py
bar/
__init__.py
test_view.py
Теперь pytest будет загружать модули как tests.foo.test_view
и tests.bar.test_view
, позволяя вам иметь модули с одинаковыми именами. Но теперь это вводит тонкую проблему: чтобы загрузить тестовые модули из каталога tests
, pytest добавляет корень репозитория к sys.path
, что добавляет побочный эффект, что теперь mypkg
также является импортируемым.
Это проблематично, если вы используете инструмент типа tox для тестирования вашего пакета в виртуальной среде, потому что вы хотите протестировать установленную версию вашего пакета, а не локальный код из репозитория.
Режим импорта importlib
не имеет ни одного из описанных выше недостатков, поскольку sys.path
не изменяется при импорте тестовых модулей.
токсины¶
Когда вы закончите работу и захотите убедиться, что ваш пакет прошел все тесты, вам может пригодиться tox, инструмент автоматизации тестирования virtualenv и его pytest support. tox поможет вам установить виртуальное окружение virtualenv с заранее определенными зависимостями, а затем выполнить предварительно настроенную команду тестирования с опциями. Он будет запускать тесты против установленного пакета, а не против вашей проверки исходного кода, помогая обнаружить сбои в упаковке.
Не запускайте через setuptools¶
Интеграция с setuptools не рекомендуется, т.е. вы не должны использовать python setup.py test
или pytest-runner
, и может перестать работать в будущем.
Это устарело, поскольку зависит от устаревших функций setuptools и использует функции, которые нарушают механизмы безопасности в pip. Например, „setup_requires“ и „tests_require“ обходят pip --require-hashes
. Для получения дополнительной информации и инструкций по миграции смотрите pytest-runner notice. См. также pypa/setuptools#1684.
setuptools намеревается remove the test command.