.. highlightlang:: python .. _`goodpractices`: Рекомендации по интеграции ============================ Установка пакета с помощью ``pip`` --------------------------------------- При разработке мы рекомендуем использовать venv_ для создания виртуального окружения и pip_ для установки вашего приложения и любых других используемых пакетов (таких как сам ``pytest``). Это гарантирует, что ваш код и окружение будут изолированы от вашей системной установки Python. Затем поместите в корень вашего проекта файл ``setup.py``, в котором ,как минимум, должен быть следующий код: .. code-block:: python from setuptools import setup, find_packages setup(name="PACKAGENAME", packages=find_packages()) Здесь ``PACKAGENAME`` - имя вашего пакета. Затем вы можете установить ваш пакет в режиме редактирования, запустив в той же директории: .. code-block:: bash pip install -e . Это позволит вам менять код исходных файлов (как тестов, так и самого приложения) и перезапускать тесты по вашему желанию. Вы выполняете аналог ``python setup.py develop`` или ``conda develop`` и устанавливаете ваш пакет, используя символическую ссылку на разрабатываемый код. .. _`test discovery`: .. _`Python test discovery`: Соглашения ``Python`` по поиску тестов ---------------------------------------- Стандартный поиск тестов ``pytest`` выполняется по следующим правилам: * Если не переданы никакие параметры, поиск начинается в `testpaths `_ (если они настроены) или в текущей директории. Кроме того, можно использовать параметры командной строки с любой комбинацией директорий, имен файлов и идентификаторов узлов. * Проводится рекурсивный поиск в директориях, если они не соответствуют шаблону `norecursedirs `_. * Ищутся файлы с именами "test_*.py" or "*_test.py", импортированные по :ref:`имени для импорта `. * Из этих файлов выбираются: * функции и методы с префиксом "test", расположенные вне классов * тестовые функции и методы с префиксом "test" внутри классов с префиксом "Test" (без метода "__init__") Пример настройки поиска тестов: :ref:`Изменение стандартных правил поиска тестов Python `. В модулях ``Python`` ``pytest`` для поиска тестов также использует стандартный механизм подклассов :ref:`unittest.TestCase `. Выбор шаблона размещения и правила импорта тестов --------------------------------------------------- ``pytest`` поддерживает два способа размещения тестов: Тесты вне кода самого приложения ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Размещение тестов в отдельном каталоге вне фактического кода приложения может быть полезно, если у вас много функциональных тестов или же по каким-то причинам вы хотите держать тесты отдельно от фактического кода приложения (зачастую это - хорошая идея): .. code-block:: text 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``. .. note:: См. :ref:`pytest vs python -m pytest` чтобы узнать больше о разнице между вызовом ``pytest`` и ``python -m pytest``. Обратите внимание, что при использовании такой схемы ваши тестовые файлы должны иметь **уникальные имена**, поскольку ``pytest`` просто добавляет директорию ``tests/`` в ``sys.path`` и полное имя файла будет представлять собой просто имя вашего модуля. Если все же нужно запускать модули с одинаковыми именами, можно добавить файл ``__init__.py`` в папку ``tests`` и ее подпапки, разбив ваши тесты на пакеты: .. code-block:: text 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``, когда корневой пакет приложения находится в поддиректории корневой директории: .. code-block:: text 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ș `_. Тесты как часть кода приложения ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Встроенные в пакет приложения тесты имеют смысл, если у вас есть прямая связь между тестами и модулями приложения, и если вы хотите поставлять тесты вместе с приложением. .. code-block:: text setup.py mypkg/ __init__.py app.py view.py test/ __init__.py test_app.py test_view.py ... В этом случае проще всего запустить тесты, используя опцию ``--pyargs``: .. code-block:: bash pytest --pyargs mypkg ``pytest`` найдет, где установлен ``mypkg`` и соберет тесты оттуда. Обратите внимание, что такой способ тоже работает с шаблоном ``src``, упомянутом в предыдущем разделе. .. note:: Вы можете использовать пространство имен Python3 (PEP420) для своего приложения, но ``pytest`` все равно будет искать :ref:`имя для импорта `, основываясь на наличии файлов "__init__.py". Если вы используете один из рекумендуемых способов формирования файловой системы, но не хотите использовать файлы "__init__.py" в ваших директориях, эта схема будет работать на версиях ``Python3.3`` и выше. Однако при "встроенных" тестах вам придется использовать абсолютный импорт, чтобы добраться до кода вашего приложения. .. _`test package name`: .. note:: Когда ``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``, где путь определяется преобразованием разделителей ``/`` в символы ``.``. Это означает, что нужно сопоставлять именя файлов и директорий импортируемым именам напрямую. Причина такой эволюции метода импорта заключается в том, что в крупных проектах тестовые модули могут импортировать друг друга, и получение канонического имени импорта помогает избежать сюрпризов (например, двойного импорта тестового модуля). .. _`virtualenv`: https://pypi.org/project/virtualenv/ .. _`buildout`: http://www.buildout.org/ .. _pip: https://pypi.org/project/pip/ .. _`use tox`: tox ------ Если работа выполнена и вы хотите убедиться, что готовый пакет проходит все тесты, можно обратить внимание на `tox`_ - инструмент автоматизации тестирования и его `поддержку pytest `_. ``tox`` помогает настроить виртуальное окружение с заранее заданными зависимостями и затем запускать предварительно настроенную команду тестирования с различными опциями. ``tox`` тестирует установленный пакет, а не источник кода, тем самым помогая найти погрешности сборки. .. include:: links.inc