Рекомендации по интеграции¶
Установка пакета с помощью pip
¶
При разработке мы рекомендуем использовать venv для создания виртуального окружения и pip
для установки вашего приложения и любых других используемых пакетов (таких как сам pytest
).
Это гарантирует, что ваш код и окружение будут изолированы от вашей системной установки Python.
Затем поместите в корень вашего проекта файл setup.py
, в котором ,как минимум, должен быть
следующий код:
from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())
Здесь PACKAGENAME
- имя вашего пакета. Затем вы можете установить ваш пакет в режиме редактирования,
запустив в той же директории:
pip install -e .
Это позволит вам менять код исходных файлов (как тестов, так и самого приложения) и перезапускать тесты
по вашему желанию. Вы выполняете аналог python setup.py develop
или conda develop
и
устанавливаете ваш пакет, используя символическую ссылку на разрабатываемый код.
Соглашения Python
по поиску тестов¶
Стандартный поиск тестов pytest
выполняется по следующим правилам:
Если не переданы никакие параметры, поиск начинается в testpaths (если они настроены) или в текущей директории. Кроме того, можно использовать параметры командной строки с любой комбинацией директорий, имен файлов и идентификаторов узлов.
Проводится рекурсивный поиск в директориях, если они не соответствуют шаблону norecursedirs.
Ищутся файлы с именами «test_*.py» or «*_test.py», импортированные по имени для импорта.
Из этих файлов выбираются:
функции и методы с префиксом «test», расположенные вне классов
тестовые функции и методы с префиксом «test» внутри классов с префиксом «Test» (без метода «__init__»)
Пример настройки поиска тестов: Изменение стандартных правил поиска тестов Python.
В модулях Python
pytest
для поиска тестов также использует стандартный механизм подклассов
unittest.TestCase.
Выбор шаблона размещения и правила импорта тестов¶
pytest
поддерживает два способа размещения тестов:
Тесты вне кода самого приложения¶
Размещение тестов в отдельном каталоге вне фактического кода приложения может быть полезно, если у вас много функциональных тестов или же по каким-то причинам вы хотите держать тесты отдельно от фактического кода приложения (зачастую это - хорошая идея):
setup.py
mypkg/
__init__.py
app.py
view.py
tests/
test_app.py
test_view.py
...
Такой способ дает вам следующие преимущества:
Вы можете тестировать установленную версию приложения после выполнения
pip install
.Вы можете тестировать локальную копию приложения в режиме редактирования после выполнения
pip install --editable
.Если у вас нет
setup.py
-файла и вы пользуетесь тем, чтоPython
по умолчанию помещает текущий каталог вsys.path
для импорта вашего пакета, вы можете выполнитьpython -m pytest
, чтобы запустить тестирование локальной копии напрямую, без использованияpip
.
Примечание
См. sys.path/PYTHONPATH и механизм импорта pytest чтобы узнать больше о разнице между вызовом pytest
и python -m pytest
.
Обратите внимание, что при использовании такой схемы ваши тестовые файлы должны иметь
уникальные имена, поскольку pytest
просто добавляет директорию tests/
в sys.path
и полное имя файла будет представлять собой просто имя вашего модуля.
Если все же нужно запускать модули с одинаковыми именами, можно добавить файл __init__.py
в
папку tests
и ее подпапки, разбив ваши тесты на пакеты:
setup.py
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,
поскольку вам нужно тестировать установленную версию пакета, а не локальный код репозитария.
В таких случаях настоятельно рекомендуется использовать шаблон src
, когда
корневой пакет приложения находится в поддиректории корневой директории:
setup.py
src/
mypkg/
__init__.py
app.py
view.py
tests/
__init__.py
foo/
__init__.py
test_view.py
bar/
__init__.py
test_view.py
Использование такого шаблона помогает обойти множество подводных камней и имеет массу преимуществ, которые прекрасно освещены в замечательном блоге: blog post by Ionel Cristian Mărieș.
Тесты как часть кода приложения¶
Встроенные в пакет приложения тесты имеют смысл, если у вас есть прямая связь между тестами и модулями приложения, и если вы хотите поставлять тесты вместе с приложением.
setup.py
mypkg/
__init__.py
app.py
view.py
test/
__init__.py
test_app.py
test_view.py
...
В этом случае проще всего запустить тесты, используя опцию --pyargs
:
pytest --pyargs mypkg
pytest
найдет, где установлен mypkg
и соберет тесты оттуда.
Обратите внимание, что такой способ тоже работает с шаблоном src
,
упомянутом в предыдущем разделе.
Примечание
Вы можете использовать пространство имен Python3 (PEP420) для своего приложения,
но pytest
все равно будет искать имя для импорта,
основываясь на наличии файлов «__init__.py». Если вы используете один из рекумендуемых
способов формирования файловой системы, но не хотите использовать файлы «__init__.py»
в ваших директориях, эта схема будет работать на версиях Python3.3
и выше.
Однако при «встроенных» тестах вам придется использовать абсолютный импорт,
чтобы добраться до кода вашего приложения.
Примечание
Когда 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
, где путь определяется преобразованием разделителей/
в символы.
. Это означает, что нужно сопоставлять именя файлов и директорий импортируемым именам напрямую.
Причина такой эволюции метода импорта заключается в том, что в крупных проектах тестовые модули могут импортировать друг друга, и получение канонического имени импорта помогает избежать сюрпризов (например, двойного импорта тестового модуля).
tox¶
Если работа выполнена и вы хотите убедиться, что готовый пакет проходит
все тесты, можно обратить внимание на tox - инструмент автоматизации тестирования
и его поддержку pytest.
tox
помогает настроить виртуальное окружение с заранее заданными
зависимостями и затем запускать предварительно настроенную команду тестирования
с различными опциями. tox
тестирует установленный пакет, а не источник кода,
тем самым помогая найти погрешности сборки.