Передовая практика интеграции

Установите пакет с помощью 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.

Вернуться на верх