Хабрахабр

[Перевод] Python Testing с pytest. Плагины, ГЛАВА 5

Вернуться Дальше

Кодовая база pytest структурирована настройками и расширениями, и есть хуки, доступные для модификации и улучшений через плагины. Достаточно мощный pytest прямо из коробки, становится еще лучше, когда вы добавляете в него микс из плагинов.

6 и pytest 3. Примеры в этой книге написаны с использованием Python 3. pytest 3. 2. 6, 2. 2 поддерживает Python 2. 3+. 7 и Python 3.

Вам не нужно загружать исходный код, чтобы понять тестовый код; тестовый код представлен в удобной форме в примерах. Исходный код для проекта Tasks, а также для всех тестов, показанных в этой книге, доступен по ссылке на веб-странице книги в pragprog.com. Там же, на веб-странице книги есть ссылка для сообщений errata и дискуссионный форум. Но что бы следовать вместе с задачами проекта, или адаптировать примеры тестирования для проверки своего собственного проекта (руки у вас развязаны!), вы должны перейти на веб-страницу книги и скачать работу.

Под спойлером приведен список статей этой серии.

Оглавление

Поехали дальше!

Каждый раз, когда вы помещаете фикстуры и/или hook-функции в файл conftest.py верхнего уровня проекта, вы создаёте локальный плагин conftest. Возможно вы удивитесь узнав, что вы уже написали какие то плагины, если вы проработали предыдущие главы в этой книге. Это просто небольшая дополнительная работа по преобразованию этих файлов conftest.py в устанавливаемые плагины, которые вы можете разделить между проектами, с другими людьми или с миром.

Довольно много плагинов доступны, так что есть приличный шанс, что кто — то уже написал изменения, которые вы хотите сделать в pytest. Мы начнем эту главу, с ответа на вопрос, где искать сторонние плагины. Хотя эта глава посвящена созданию ваших собственных плагинов, Приложение 3, плагин Sampler Pack, на странице 163 включен, чтобы дать вам почувствовать вкус того, что возможно. Так как мы будем рассматривать плагины с открытым исходным кодом, то если плагин делает почти то, что вы хотите сделать, но не совсем, вы можете развить его, или использовать его в качестве эталона для создания собственного плагина.

Полная тема упаковки и распространения Python, слишком обширна и претендует на собственную книгу, поэтому мы не будем охватывать все. В этой главе вы узнаете, как создавать плагины, и я укажу вам правильное направление для их тестирования, упаковки и распространения. Я также расскажу о некоторых простых способах для создания плагинов с поддержкой PyPI и наименьшим количеством работы. Но вы получите достаточно сведений, чтобы иметь возможность обмениваться плагинами с вашей командой.

Поиск плагинов

Плагины, перечисленные в Приложении 3, Plugin Sampler Pack, на стр. Вы можете найти сторонние плагины pytest в нескольких местах. Тем не менее, это не единственное место для поиска отличных плагинов pytest. 163, доступны для загрузки с PyPI.

https://docs.pytest.org/en/latest/plugins.html

На главном сайте документации pytest есть страница, в которой рассказывается об установке и использовании плагинов pytest и перечислены несколько распространенных плагинов.

https://pypi.python.org

При поиске плагинов pytest достаточно ввести “pytest,” “pytest -” или “-pytest” в поле поиска, так как большинство pytest плагины либо начинаются с “pytest -” или заканчивается на “-pytest.” Python Package Index (PyPI) — это отличное место для получения большого количества пакетов Python, но также отличное место для поиска плагинов pytest.

https://github.com/pytest-dev

Кроме того, здесь вы можете найти популярные плагины pytest, которые должны поддерживаться в долгосрочной перспективе командой ядра pytest. Группа «pytest-dev» на GitHub — это место, где хранится исходный код pytest.

Установка плагинов

Однако,
вы можете использовать pip несколькими способами для установки плагинов. Плагины pytest устанавливаются с pip, как и другие пакеты Python.

Установка из PyPI

Давайте установим плагин pytest-cov: Поскольку PyPI является местоположением по умолчанию для pip, установка плагинов из PyPI является самым простым методом.

$ pip install pytest-cov

Будет установлена последняя стабильная версия от PyPI.

Установка определенной версии из PyPI

Если вы хотите конкретную версию плагина, вы можете указать версию после ==:

$ pip install pytest-cov==2.4.0

Установка из файла .tar.gz или .whl

Они часто упоминаются как «tar balls» и «wheels». Пакеты на PyPI распространяются как zip-файлы с расширениями .tar.gz и/или .whl. Если у вас возникли проблемы с попыткой работать с PyPI напрямую (что может случиться с брандмауэрами и другими сетевыми осложнениями), вы можете загрузить либо .tar.gz, либо .whl и установить из этого-того.

Вам не нужно распаковывать или танцевать с бубном; просто укажите pip на него:

$ pip install pytest-cov-2.4.0.tar.gz
# or
$ pip install pytest_cov-2.4.0-py2.py3-none-any.whl

Установка из локального каталога

Вы можете иметь заначку плагинов (и других пакетов Python) в локальном или общем каталоге в формате .tar.gz или .whl и использовать это вместо PyPI для установки плагинов:

$ mkdir some_plugins
$ cp pytest_cov-2.4.0-py2.py3-none-any.whl some_plugins/
$ pip install --no-index --find-links=./some_plugins/ pytest-cov

--find-links=./some_plugins/ указывает pip искать в каталоге some_plugins. --no-index указывает pip не подключаться к PyPI. (Мы поговорим как о tox, так и о непрерывной интеграции в главе 7, используя pytest с другими инструментами, на странице 125.) Этот метод особенно полезен, если у вас есть как сторонние, так и собственные плагины, хранящиеся локально, а также если вы создаете новые виртуальные среды для непрерывной интеграции или с tox.

Обратите внимание, что с помощью метода установки локального каталога вы можете установить несколько версий и указать, какую версию вы хотите, добавив == и номер версии:

$ pip install --no-index --find-links=./some_plugins/ pytest-cov==2.4.0

Установка из репозитория Git

Вы можете установить плагины непосредственно из Git-репозитория в этом случае GitHub:

$ pip install git+https://github.com/pytest-dev/pytest-cov

Можно также указать тег версии:

$ pip install git+https://github.com/pytest-dev/pytest-cov@v2.4.0

Или можно указать ветвь:

$ pip install git+https://github.com/pytest-dev/pytest-cov@master

Установка из репозитория Git особенно полезна, если вы храните свою собственную работу в Git или если требуемая версия плагина или плагин отсутствует в PyPI.

Примечание переводчика:

pip поддерживает установку из Git, Mercurial, Subversion и Bazaar и определяет тип VCS, используя префиксы url: «git+», «hg+», «svn+», «bzr+».
Более подробно можно ознакомиться в документации PyPI

Написание собственных плагинов

Это одна из причин, по которой мы используем их-чтобы сэкономить нам время на разработку всего этого самостоятельно. Многие сторонние плагины содержат довольно много кода. Создавая плагин, вы можете легко поделиться даже несколькими фикстурами, которые вы хотите разделить между несколькими проектами. Тем не менее, для вашего конкретного кода вы, несомненно, придумаете специальные фикстуры и модификации, которые помогут вам его протестировать. Это довольно легко сделать. Вы можете поделиться этими изменениями с несколькими проектами — и, возможно, с остальным миром, — разрабатывая и распространяя свои собственные плагины. В этом разделе мы разработаем небольшую модификацию поведения pytest, упакуем ее как плагин, протестируем и рассмотрим, как ее распространять.

Поскольку pytest был разработан с целью позволить плагинам слегка менять поведение pytest, доступно множество hook-функций. Плагины могут включать hook-функции, которые изменяют поведение pytest. В нашем примере мы создадим плагин, который изменит внешний вид статуса теста. hook-и для pytest указаны на сайте документации pytest. Добавим текст в выходной заголовок. Добавим параметр командной строки, чтобы включить это новое поведение. Для этого будем использовать опцию --nice. В частности, мы изменим все индикаторы состояния FAILED (неудачный) на “OPPORTUNITY (перспективный) для усовершенствования,” изменим F на O, и добавим “Thanks for running the tests” (Спасибо за выполнение тестов) к заголовку.

Вам не нужно запускать плагины таким образом. Чтобы сохранить изменения поведения отдельно от обсуждения механики плагинов, мы внесем изменения в conftest.py до того, как превратим его в распространяемый плагин. Поэтому мы начнем с добавления функциональности в файл conftest.py, а затем, после того, как все заработает в conftest.py, переместим код в пакет. Но часто изменения, которые вы намеревались использовать только в одном проекте, станут достаточно полезными, чтобы поделиться ими и превратиться в плагин.

В разделе "ожидание исключений" на странице 30 мы написали несколько тестов, которые проверяли, вызываются ли исключения, если кто-то неправильно вызвал функцию API. Вернемся к проекту Tasks. Похоже, мы пропустили хотя бы несколько возможных состояний ошибки.

Вот еще пара тестов:

ch5/a/tasks_proj/tests/func/test_api_exceptions.py

"""Проверка ожидаемых исключений из использования API wrong."""
import pytest
import tasks
from tasks import Task @pytest.mark.usefixtures('tasks_db')
class TestAdd(): """Тесты, связанные с tasks.add().""" def test_missing_summary(self): """Следует поднять исключение, если summary missing.""" with pytest.raises(ValueError): tasks.add(Task(owner='bob')) def test_done_not_bool(self): """Должно вызвать исключение, если done не является bool.""" with pytest.raises(ValueError): tasks.add(Task(summary='summary', done='True'))

Давайте запустим их, чтобы проверить, проходят ли они:

$ cd /path/to/code/ch5/a/tasks_proj
$ pytest
===================== test session starts ====================== collected 57 items
tests/func/test_add.py ...
tests/func/test_add_variety.py ............................
tests/func/test_add_variety2.py ............
tests/func/test_api_exceptions.py .F.......
tests/func/test_unique_id.py .
tests/unit/test_task.py .... =========================== FAILURES =========================== __________________ TestAdd.test_done_not_bool __________________ self = <func.test_api_exceptions.TestAdd object at 0x103a71a20> def test_done_not_bool(self): """Should raise an exception if done is not a bool.""" with pytest.raises(ValueError): > tasks.add(Task(summary='summary', done='True'))
E Failed: DID NOT RAISE <class 'ValueError'> tests/func/test_api_exceptions.py:20: Failed ============= 1 failed, 56 passed in 0.28 seconds ==============

Поскольку вы уже видели трассировку, вы можете отключить ее, нажав --tb=no. Давайте запустим его снова с -v для подробностей.

А теперь давайте сосредоточимся на новых тестах с -k TestAdd, который работает, потому что нет никаких других тестов с именами, которые содержат “TestAdd.”

Мы могли бы "всё бросить" и попытаться исправить этот тест (и мы сделаем это позже), но сейчас мы сосредоточемся на попытке сделать ссобщения о неудаче (failures) более приятными для разработчиков.

Давайте начнем с добавления сообщения "thank you" в заголовок, который вы можете сделать с помощью хука pytest под названием pytest_report_header().

ch5/b/tasks_proj/tests/conftest.py

def pytest_report_header(): """Благодарность тестеру за выполнение тестов.""" return "Thanks for running the tests."

Однако возможность добавления информации в заголовок может быть расширена. Очевидно, что печатать благодарственное сообщение довольно глупо. Вообщем, все, что вы можете преобразовать в строку, можно вставить в заголовок теста. Можно добавить имя пользователя, указать используемое оборудование и тестируемые версии.

Есть хук, который позволяет эту интрижку: pytest_report_teststatus(): Затем мы изменим отчет о состоянии теста, чтобы изменить F на O и FAILED на OPPORTUNITY for improvement.

ch5/b/tasks_proj/tests/conftest.py

def pytest_report_teststatus(report): """Превращает неудачи в возможности.""" if report.when == 'call' and report.failed: return (report.outcome, 'O', 'OPPORTUNITY for improvement')

Тестовый сеанс без флага --verbose показывает O для сбоев, то есть, возможности улучшения: И теперь мы имеем как раз выход мы искали.

$ cd /path/to/code/ch5/b/tasks_proj/tests/func
$ pytest --tb=no test_api_exceptions.py -k TestAdd ===================== test session starts ====================== Thanks for running the tests.
collected 9 items
test_api_exceptions.py .O ====================== 7 tests deselected ======================
======= 1 failed, 1 passed, 7 deselected in 0.06 seconds =======

С флагом -v или --verbose будет получше:

$ pytest -v --tb=no test_api_exceptions.py -k TestAdd
===================== test session starts ====================== Thanks for running the tests.
collected 9 items
test_api_exceptions.py::TestAdd::test_missing_summary PASSED
test_api_exceptions.py::TestAdd::test_done_not_bool OPPORTUNITY for improvement ====================== 7 tests deselected ======================
======= 1 failed, 1 passed, 7 deselected in 0.07 seconds =======

Последнее изменение, которое мы сделаем, это добавим параметр командной строки, --nice, чтобы изменения нашего статуса происходили, только если подставить --nice:

def pytest_addoption(parser): """Включает nice функцию с опцией --nice.""" group = parser.getgroup('nice') group.addoption("--nice", action="store_true", help="nice: turn failures into opportunities") def pytest_report_header(): """Благодарность тестеру за выполнение тестов.""" if pytest.config.getoption('nice'): return "Thanks for running the tests." def pytest_report_teststatus(report): """Превращает неудачи в возможности.""" if report.when == 'call': if report.failed and pytest.config.getoption('nice'): return (report.outcome, 'O', 'OPPORTUNITY for improvement')

Есть много других, которые можно найти на главном сайте документации Pytest. Стоит заметить, что для этого плагина мы используем только пару хуков.

Во-первых, без опции --nice, чтобы убедиться, что отображается только имя пользователя: Теперь мы можем вручную протестировать наш плагин, просто запустив его в нашем примере.

$ cd /path/to/code/ch5/c/tasks_proj/tests/func
$ pytest --tb=no test_api_exceptions.py -k TestAdd ===================== test session starts ====================== collected 9 items
test_api_exceptions.py .F ====================== 7 tests deselected ======================
======= 1 failed, 1 passed, 7 deselected in 0.07 seconds =======

Теперь с --nice:

$ pytest --nice --tb=no test_api_exceptions.py -k TestAdd ===================== test session starts ====================== Thanks for running the tests.
collected 9 items
test_api_exceptions.py .O ====================== 7 tests deselected ======================
======= 1 failed, 1 passed, 7 deselected in 0.07 seconds =======

Теперь с --nice и --verbose:

$ pytest -v --nice --tb=no test_api_exceptions.py -k TestAdd ===================== test session starts ====================== Thanks for running the tests.
collected 9 items
test_api_exceptions.py::TestAdd::test_missing_summary PASSED
test_api_exceptions.py::TestAdd::test_done_not_bool OPPORTUNITY for improvement ====================== 7 tests deselected ======================
======= 1 failed, 1 passed, 7 deselected in 0.06 seconds =======

Все изменения, которые мы хотели сделать, сделаны примерно в десятке строк кода файла conftest.py. Отлично! Далее мы переместим этот код в структуру плагина.

Создание устанавливаемого плагина

Даже если вы никогда не включите свой собственный плагин в PyPI, то пройдя через этот процесс, вам будет легче читать код из плагинов с открытым исходным кодом, и у вас будет больше возможностей оценить, помогут они вам или нет. Процесс обмена плагинами с другими пользователями четко определен.

Тут и здесь и еще здесь на русском. Было бы излишним полностью охватывать packaging и distribution пакетов Python в этой книге, так как эта тема хорошо документирована в другом месте. , Тем не менее, перейти от локального подключаемого модуля конфигурации, который мы создали в предыдущем разделе, к чему-то устанавливаемому с помощью pip, является несложной задачей.

Неважно, как вы это называете, но, поскольку мы создаем плагин для флага «nice», давайте назовем его «pytest-nice». Во-первых, нам нужно создать новый каталог для размещения нашего кода плагина. (Каталог тестов будет обсуждаться в разделе «Плагины тестирования» на странице. У нас будет два файла в этом новом каталоге: pytest_nice.py и setup.py. 105.)

│ LICENSE
│ pytest_nice.py
│ setup.py

└───tests │ conftest.py │ test_nice.py

В pytest_nice.py, мы поместим точное содержимое нашего conftest.py, которое было связано с этой функцией (и извлечем его из tasks_proj/tests/conftest.py):

ch5/pytest-nice/pytest_nice.py

"""Код для pytest-nice плагин.""" import pytest def pytest_addoption(parser): """Включает nice функцию с опцией --nice.""" group = parser.getgroup('nice') group.addoption("--nice", action="store_true", help="nice: turn FAILED into OPPORTUNITY for improvement") def pytest_report_header(): """Благодарность тестеру за выполнение тестов.""" if pytest.config.getoption('nice'): return "Thanks for running the tests." def pytest_report_teststatus(report): """Превращает неудачи в возможности.""" if report.when == 'call': if report.failed and pytest.config.getoption('nice'): return (report.outcome, 'O', 'OPPORTUNITY for improvement')

В setup.py нам нужен максимальноминимальный вызов setup():

ch5/pytest-nice/setup.py

"""Setup для pytest-nice plugin.""" from setuptools import setup setup( name='pytest-nice', version='0.1.0', description='Плагин Pytest, чтобы включить FAILURE into OPPORTUNITY', url='https://место/где/содержится/информация/на/этот/пакет', author='Ваше имя', author_email='your_email@somewhere.com', license='proprietary', py_modules=['pytest_nice'], install_requires=['pytest'], entry_points=,
)

Однако для небольшой команды или просто для себя этого будет достаточно. Вам понадобится больше информации в настройках, если вы собираетесь распространять ее среди широкой аудитории или в интернете.

Поле версии является версией этого плагина. Вы можете включить еще какие то параметры для setup(); а тут у нас только обязательные поля. Поле URL обязательно для заполнения. И это целиком зависит от вас, когда вы поднимаете версию. Поля author и author_email можно заменить на maintainer и maintainer_email, но одна из этих пар должна быть там. Вы можете оставить его пустым, но вы получите предупреждение. Это может быть одна из многих лицензий с открытым исходным кодом, ваше имя или компании, или что-то подходящее для вас. Поле license-лицензия представляет собой короткое текстовое поле. Хотя это список, и вы можете включить более одного модуля, если бы у меня было больше одного, я бы использовал пакет и поместил все модули в один каталог. Запись py_modules перечисляет pytest_nice как наш единственный модуль для этого плагина.

Частью, которая отличается для плагинов Pytest, является параметр entry_points. До сих пор все параметры setup() являются стандартными и используются для всех инсталляторов Python. В этой строке мы сообщаем pytest, что nice-это имя нашего плагина, а pytest_nice-имя модуля, в котором живет наш плагин. Мы перечислили entry_points={'pytest11': ['nice = pytest_nice', ], },. Функция entry_points является стандартной для setuptools, но pytest11 специальный идентификатор, который ищет pytest. Если бы мы использовали пакет, наша запись здесь была бы:

Некоторая форма README является требованием setuptools. Я еще не говорил о файле README.rst. Если вы пропустите его, вы получите это:

...
warning: sdist: standard file not found: should have one of README,
README.rst, README.txt
...

Вот что я положил в файл для pytest-nice: Сохранение README в качестве стандартного способа включения некоторой информации о проекте-хорошая идея в любом случае.

ch5/pytest-nice/README.rst

pytest-nice : A pytest plugin
============================= Делает вывод pytest немного дружелюбнее во время сбоев. Особенности
--------
- Включает имя пользователя, выполняющего тесты в выводе pytest.
- Добавляет ``--nice`` опцию, которая:
- превращает ``F`` в ``O``
- с ``-v``, преобразует ``FAILURE`` в ``OPPORTUNITY for improvement`` Установка ------------ Учитывая, что наши плагины Pytest сохраняются в виде .tar.gz в
общей директория PATH, устанавливайте так: :: $ pip install PATH/pytest-nice-0.1.0.tar.gz
$ pip install --no-index --find-links PATH pytest-nice Использование
----- :: $ pytest --nice

Это сильно обрезанная версия, но она работает. Есть много мнений о том, что должно быть в файле README.

Тестирование Плагинов

Тем не менее, тестирование изменений в инструменте тестирования немного сложнее. Плагины — это код, который необходимо протестировать, как и любой другой код. Мы можем сделать то же самое в автоматическом режиме с помощью плагина под названием pytester, который поставляется с pytest, но отключен по умолчанию. Когда мы разработали код плагина в разделе «Написание собственных плагинов», на странице 98, мы проверили его вручную, используя образец тестового файла, запустив с ним pytest и проверив вывод, чтобы убедиться, что он был правильным.

Чтобы использовать pytester, нам нужно добавить только одну строку в conftest.py: В нашем тестовом каталоге для pytest-nice есть два файла: conftest.py и test_nice.py.

ch5/pytest-nice/tests/conftest.py

"""pytester is needed for testing plugins."""
pytest_plugins = 'pytester'

Мы будем использовать фикстуру под названием testdir, которая становится доступным, когда pytester включен.
Часто тесты для плагинов принимают форму, которую мы описали вручную: Эта строка включает плагин pytester.

  1. Сделайте пример тестового файла.
  2. Запустите pytest с некоторыми параметрами или без них в каталоге, который содержит файл примера.
  3. Проверьте выходные данные.
  4. Возможный, для проверки кода результат-0 для всех проходов, 1 для некоторых сбоев.

Давайте рассмотрим один пример:

ch5/pytest-nice/tests/test_nice.py

def test_pass_fail(testdir): # создать временный тестовый модуль Pytest testdir.makepyfile(""" def test_pass(): assert 1 == 1 def test_fail(): assert 1 == 2 """) # запустить pytest result = testdir.runpytest() # fnmatch_lines выполняет внутренний ассерт result.stdout.fnmatch_lines([ '*.F', # . для Pass, F для Fail ]) # убедитесь, что мы получили код выхода '1' для testsuite assert result.ret == 1

Она имеет метод makepyfile(), который позволяет поместить содержимое тестового файла.В этом случае мы создаем два теста: один, который проходит и другой, который не проходит. Фикстура testdir автоматически создает временный каталог для размещения тестовых файлов.

Вы можете передать параметры, если хотите. Мы запускаем pytest для нового тестового файла с помощью testdir.runpytest(). Возвращаемое значение может быть рассмотрено в дальнейшем и имеет тип RunResult.

Для проверки вывода, по аналогии с тем, как мы это делали вручную, используйте fnmatch_lines, передав список строк, которые мы хотим видеть в выводе, а затем убедившись, что ret равен 0 для проходящих сеансов и 1 для неудачных сеансов. Обычно я смотрю на stdout и ret. Мы можем использовать наш пример файла для тестов. Строки, передаваемые в fnmatch_lines, могут включать символы подстановки. Вместо того, чтобы дублировать этот код, давайте напишем фикстуру:

ch5/pytest-nice/tests/test_nice.py

@pytest.fixture()
def sample_test(testdir): testdir.makepyfile(""" def test_pass(): assert 1 == 1 def test_fail(): assert 1 == 2 """) return testdir

Ниже приведены тесты для других вариантов: Теперь, для остальных тестов, мы можем использовать sample_test в качестве каталога, который уже содержит наш пример тестового файла.

ch5/pytest-nice/tests/test_nice.py

def test_with_nice(sample_test): result = sample_test.runpytest('--nice') result.stdout.fnmatch_lines(['*.O', ]) # . for Pass, O for Fail assert result.ret == 1 def test_with_nice_verbose(sample_test): result = sample_test.runpytest('-v', '--nice') result.stdout.fnmatch_lines([ '*::test_fail OPPORTUNITY for improvement', ]) assert result.ret == 1 def test_not_nice_verbose(sample_test): result = sample_test.runpytest('-v') result.stdout.fnmatch_lines(['*::test_fail FAILED']) assert result.ret == 1

Давайте убедимся, что наше благодарственное сообщение находится в заголовке: Осталось написать еще пару тестов.

ch5/pytest-nice/tests/test_nice.py

def test_header(sample_test): result = sample_test.runpytest('--nice') result.stdout.fnmatch_lines(['Thanks for running the tests.']) def test_header_not_nice(sample_test): result = sample_test.runpytest() thanks_message = 'Thanks for running the tests.' assert thanks_message not in result.stdout.str()

Это могло бы быть частью других тестов, но мне нравится проводить его в отдельном тесте, чтобы один тест проверял одну задачу.

Давайте проверим текст справки:

ch5/pytest-nice/tests/test_nice.py

def test_help_message(testdir): result = testdir.runpytest('--help') # fnmatch_lines делает внутренний ассерт result.stdout.fnmatch_lines([ 'nice:', '*--nice*nice: turn FAILED into OPPORTUNITY for improvement', ])

Я думаю, что это достаточно хорошая проверка, того, что наш плагин работает.

Мы делаем это либо установкой файла .zip.gz, либо установкой текущего каталога в редактируемом режиме: Для запуска тестов давайте начнем с нашего каталога pytest-nice и убедимся, что наш плагин установлен.

$ cd /path/to/code/ch5/pytest-nice/
$ pip install . Processing /path/to/code/ch5/pytest-nice
Requirement already satisfied: pytest in
/path/to/venv/lib/python3.6/site-packages (from pytest-nice==0.1.0)
Requirement already satisfied: py>=1.4.33 in
/path/to/venv/lib/python3.6/site-packages (from pytest->pytest-nice==0.1.0)
Requirement already satisfied: setuptools in
/path/to/venv/lib/python3.6/site-packages (from pytest->pytest-nice==0.1.0)
Building wheels for collected packages: pytest-nice
Running setup.py bdist_wheel for pytest-nice ... done
...
Successfully built pytest-nice
Installing collected packages: pytest-nice
Successfully installed pytest-nice-0.1.0

Теперь, когда он установлен, давайте запустим тесты:

$ pytest -v
===================== test session starts ======================
plugins: nice-0.1.0
collected 7 items
tests/test_nice.py::test_pass_fail PASSED
tests/test_nice.py::test_with_nice PASSED
tests/test_nice.py::test_with_nice_verbose PASSED
tests/test_nice.py::test_not_nice_verbose PASSED
tests/test_nice.py::test_header PASSED
tests/test_nice.py::test_header_not_nice PASSED
tests/test_nice.py::test_help_message PASSED
=================== 7 passed in 0.34 seconds ===================

переводчика: Если вы потерпели неудачу, то вы не одиноки. Прим. У меня тесты не прошли сразу.

platform win32 -- Python 3.6.5, pytest-3.9.3, py-1.7.0, pluggy-0.8.0 -- c:\venv36\scripts\python.exe collected 7 items tests/test_nice.py::test_pass_fail FAILED [ 14%]
tests/test_nice.py::test_with_nice OPPORTUNITY for improvement [ 28%]
tests/test_nice.py::test_with_nice_verbose OPPORTUNITY for improvement [ 42%]
tests/test_nice.py::test_not_nice_verbose FAILED [ 57%]
tests/test_nice.py::test_header PASSED [ 71%]
tests/test_nice.py::test_header_not_nice PASSED [ 85%]
tests/test_nice.py::test_help_message PASSED [100%] ================================== FAILURES ===================================
_______________________________ test_pass_fail ________________________________

Я исправил шаблон поиска

result.stdout.fnmatch_lines([ '*.F', # . for Pass, F for Fail ])

на

result.stdout.fnmatch_lines([ '*.F*', # . for Pass, F for Fail ])

Добавил символ * после F

По аналогии я внес исправления в test_with_nice, test_with_nice_verbose, test_not_nice_verbose

O [100%]'
Здесь после идут пробелы и проценты в квадратных скобках
Кроме того, я получил сообщения Видимо причина в версии pytest.
Я получаю вывод c процентом вида
'test_with_nice.py .

Class is deprecated, please use pytest. RemovedInPytest4Warning: usage of Session. Class instead

В остальном всё нормуль!

(venv36) c:\_BOOKS_\pytest_si\bopytest-code\code\ch5\pytest-nice>pytest -v
============================= test session starts =============================
platform win32 -- Python 3.6.5, pytest-3.9.3, py-1.7.0, pluggy-0.8.0 -- c:\venv36\scripts\python.exe
cachedir: .pytest_cache
rootdir: c:\_BOOKS_\pytest_si\bopytest-code\code\ch5\pytest-nice, inifile:
plugins: nice-0.1.0
collected 7 items tests/test_nice.py::test_pass_fail PASSED [ 14%]
tests/test_nice.py::test_with_nice PASSED [ 28%]
tests/test_nice.py::test_with_nice_verbose PASSED [ 42%]
tests/test_nice.py::test_not_nice_verbose PASSED [ 57%]
tests/test_nice.py::test_header PASSED [ 71%]
tests/test_nice.py::test_header_not_nice PASSED [ 85%]
tests/test_nice.py::test_help_message PASSED [100%] ============================== warnings summary ===============================
tests/test_nice.py::test_pass_fail c:\venv36\lib\site-packages\_pytest\compat.py:332: RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead return getattr(object, name, default)

Все тесты пройдены. Ура! Мы можем удалить его (pytest-nice), как и любой другой пакет Python
или pytest-плагин:

$ pip uninstall pytest-nice Uninstalling pytest-nice-0.1.0: Would remove: \path\to\venv\lib\site-packages\pytest_nice-0.1.0.dist-info\* ...
Proceed (y/n)? y Successfully uninstalled pytest-nice-0.1.0

Отличный способ узнать больше о тестировании плагинов — посмотреть на тесты, содержащиеся в других плагинах pytest, доступных через PyPI.

Создание дистрибутива

Теперь, из командной строки мы можем использовать этот файл setup.py для создания дистрибутива: Верьте или нет но, мы почти закончили с нашим плагином.

$ cd /path/to/code/ch5/pytest-nice
$ python setup.py sdist
running sdist
running egg_info
creating pytest_nice.egg-info
...
running check
creating pytest-nice-0.1.0
...
creating dist
Creating tar archive
... $ ls dist pytest-nice-0.1.0.tar.gz

(Обратите внимание, что sdist означает source distribution — “распространение исходного кода.”)

1. В pytest-nice каталог dist содержит новый файл с именем pytest-nice-0. 0.tar.gz.

Этот файл теперь может быть использован в любом месте, чтобы установить наш плагин, даже на месте:

$ pip install dist/pytest-nice-0.1.0.tar.gz
Processing ./dist/pytest-nice-0.1.0.tar.gz
...
Installing collected packages: pytest-nice
Successfully installed pytest-nice-0.1.0

Теперь вы можете поместить свои файлы .tar.gz в любое место, где сможете их использовать и делиться ими.

Распространение плагинов через общий каталог

Допустим, мы поместили pytest-nice-0. pip уже поддерживает установку пакетов из общих каталогов, поэтому все, что нам нужно сделать, чтобы распространить наш плагин через общий каталог, это выбрать место, которое мы можем запомнить, и поместить туда файлы .tar.gz для наших плагинов. 0.tar.gz в каталог с именем myplugins. 1.

Чтобы установить pytest-nice из myplugins:

$ pip install --no-index --find-links myplugins pytest-nice

And of course, pytest-nice is what we want to install.
--find-links myplugins указывает PyPI найти в myplugins пакеты для установки. --no-index указывает pip не выходить на PyPI, чтобы искать то, что вы хотите установить.
The --find-links myplugins tells PyPI to look in myplugins for packages to install. И конечно, pytest-nice — это то, что мы хотим установить.

Если вы исправили какие то ошибки и в myplugins есть более новые версии, вы можете обновить их, добавив --upgrade:

$ pip install --upgrade --no-index --find-links myplugins pytest-nice

Это аналогично любому другому использованию pip, но с добавлением --no-index --find-links myplugins.

Распространение плагинов через PyPI

На самом деле, есть еще несколько шагов. Если вы хотите поделиться своим плагином с миром, есть еще пару шагов, которые мы должны сделать. Однако, поскольку эта книга не посвящена работе с открытым исходным кодом, я рекомендую ознакомиться с подробными инструкциями, содержащимися в руководстве пользователя Python Packaging.

Когда вы добавляете плагин pytest, есть отличное место для начала — использование cookiecutter-pytest-plugin:

$ pip install cookiecutter
$ cookiecutter https://github.com/pytest-dev/cookiecutter-pytest-plugin

Затем он создает каталог для вас, чтобы исследовать и заполнить ваш код. Этот проект сначала задаст вам несколько вопросов о вашем плагине. Он поддерживается основными разработчиками pytest, и они будут следить за тем, чтобы этот проект оставался в актуальном состоянии. Углубление в принципы его работы выходит за рамки этой книги; однако, пожалуйста, имейте это в виду проект.

Упражнения

Вы также использовали её в упражнениях Главы 4. В ch4/cache/test_slower.py есть autouse fixture, называемая check_duration(). Теперь давайте сделаем плагин из неё.

  1. Создайте каталог с именем pytest-slower, в котором будет храниться код для нового плагина, аналогично каталогу, описанному в разделе «Создание устанавливаемого плагина» на стр. 102.
  2. Заполните все файлы каталога, чтобы сделать pytest-slower плагином, который можно установить.
  3. Напишите некоторый тестовый код для плагина.
  4. Взгляните на Python Package Index и поищите «pytest-». Найдите плагин pytest, который выглядит интересным для вас.
  5. Установите выбранный вами плагин и попробуйте его на тестах Tasks.

Что дальше

Существуют также файлы конфигурации, которые влияют на выполнение pytest, например pytest.ini. Вы до сих пор много раз использовали conftest.py в этой книге. В следующей главе вы ознакомитесь с различными конфигурационными файлами и узнаете, что можно сделать, чтобы облегчить себе жизнь при тестировании.

Вернуться Дальше

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть