Хабрахабр

[Перевод] Python Testing с pytest. Начало работы с pytest, Глава 1

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

Это уже приносит мне дивиденды в моей компании.

Chris Shaver
VP of Product, Uprising Technology Я обнаружил, что Python Testing с pytest является чрезвычайно полезным вводным руководством к среде тестирования pytest.

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

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

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

Оглавление

Поехали

Это тест:

ch1/test_one.py

def test_passing(): assert (1, 2, 3) == (1, 2, 3)

Вот как это выглядит при запуске:

$ cd /path/to/code/ch1
$ pytest test_one.py
===================== test session starts ======================
collected 1 items
test_one.py .
=================== 1 passed in 0.01 seconds ===================

Если вам нужна дополнительная информация, вы можете использовать -v или же --verbose: Точка после test_one.py означает, что один тест был запущен, и он прошел.

$ pytest -v test_one.py
===================== test session starts ======================
collected 1 items
test_one.py::test_passing PASSED
=================== 1 passed in 0.01 seconds ===================

Прекрасно! Если у вас есть цветной терминал, PASSED и нижняя строка зеленые.

Это неудачный тест:

ch1/test_two.py

def test_failing():
assert (1, 2, 3) == (3, 2, 1)

Давайте посмотрим, что из этого выйдет: То, как pytest показывает сбои при тестирование, является одной из многих причин, почему разработчики любят pytest.

$ pytest test_two.py
===================== test session starts ======================
collected 1 items
test_two.py F
=========================== FAILURES ===========================
_________________________ test_failing _________________________
def test_failing():
> assert (1, 2, 3) == (3, 2, 1)
E assert (1, 2, 3) == (3, 2, 1)
E At index 0 diff: 1 != 3
E Use -v to get the full diff
test_two.py:2: AssertionError
=================== 1 failed in 0.04 seconds ===================

Пробный тест test_failing получает свой раздел, чтобы показать нам, почему он не прошел. Отлично!

И pytest точно сообщает, что первый сбой: index 0 — это несоответствие.

Значительная часть этого сообщения красного цвета, что делает его действительно выделяющимся (если у вас есть цветной терминал).

Это уже достаточно много информации, но есть строка с подсказкой, которая говорит Use -v, чтобы получить еще больше описаний различий.

Давайте заюзаем этот самый -v:

$ pytest -v test_two.py
===================== test session starts ======================
collected 1 items
test_two.py::test_failing FAILED
=========================== FAILURES ===========================
_________________________ test_failing _________________________
def test_failing():
> assert (1, 2, 3) == (3, 2, 1)
E assert (1, 2, 3) == (3, 2, 1)
E At index 0 diff: 1 != 3
E Full diff:
E - (1, 2, 3)
E ? ^ ^
E + (3, 2, 1)
E ? ^ ^
test_two.py:2: AssertionError
=================== 1 failed in 0.04 seconds ===================

Там, откуда это прилетело, чудес намного больше. Вот это да!
pytest добавляет символ "карет" (^), чтобы показать нам в чем именно отличие.
Если вы уже впечатлены тем, как легко писать, читать и запускать тесты с pytest и как легко читать выходные данные, чтобы увидеть, где случилась неудачка, ну… вы еще ничего не видели. Оставайтесь и позвольте мне показать вам, почему я думаю, что pytest-это абсолютно лучшая тестовая платформа.

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

Извините, что тестирую assert (1, 2, 3) == (3, 2, 1), это так скучно. Но сначала я должен извиниться. Никто бы не написал такой тест в реальной жизни. Я слышу храп?! А (1, 2, 3) == (1, 2, 3) всегда будет работать. Тесты программного обеспечения состоят из кода, который проверяет другое программное обеспечение, которое увы не всегда будет работать положительно. Мы рассмотрим тесты для реального программного проекта. Вот почему мы не будем использовать слишком глупые тесты, подобные этому, в остальной части книги. Надеюсь, это достаточно просто, чтобы быть легко понято, но не так просто, чтобы быть скучным. Мы будем использовать пример проекта Tasks, которому требуется тестовый код.

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

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

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

Вот задача:

from collections import namedtuple
Task = namedtuple('Task', ['summary', 'owner', 'done', 'id'])

Before we jump into the examples, let’s take a step back and talk about how to get pytest and install it.

6, но я все еще обнаруживаю, что многие разработчики Python не знают, насколько это круто. Функция factory namedtuple() существует с Python 2. По крайней мере, использование задачи для тестовых примеров будет интереснее, чем (1, 2, 3) == (1, 2, 3) или (1, 2)==3.

Прежде чем перейти к примерам, давайте сделаем шаг назад и поговорим о том, где взять pytest и как установить его.

Добываем pytest

Это официальная документация. Штаб-квартира pytest https://docs.pytest.org. Но распространяется он через PyPI (индекс пакета Python) в https://pypi.python.org/pypi/pytest.

Как и другие пакеты Python, распространяемые через PyPI, используйте pip для установки pytest в виртуальную среду, используемую для тестирования:

$ pip3 install -U virtualenv
$ python3 -m virtualenv venv
$ source venv/bin/activate
$ pip install pytest

Ознакомьтесь с Приложением 1 «Виртуальные среды» на стр. Если вы не знакомы с virtualenv или pip, я вас познакомлю. 155 и в Приложении 2, на странице 159.

Как насчет Windows, Python 2 и venv?

7. Пример для virtualenv и pip должен работать на многих POSIX системах, таких как Linux и macOS, а также на многих версиях Python, включая Python 2. 9 и более поздних.

Источник venv/bin/activate в строке не будет работать для Windows, используйте вместо этого venv\Scripts\activate.bat.
Выполните это:

C:\> pip3 install -U virtualenv
C:\> python3 -m virtualenv venv
C:\> venv\Scripts\activate.bat
(venv) C:\> pip install pytest

6 и выше, вы можете обойтись venv вместо virtualenv, и вы не должны беспокоиться о том что бы установить его в первую очередь. Для Python 3. 6 и выше. Он включен в Python 3. Тем не менее, я слышал, что некоторые платформы по-прежнему ведут себя лучше с virtualenv.

Запускаем pytest

$ pytest --help
usage: pytest [options] [file_or_dir] [file_or_dir] [...]
...

For example, let’s create a subdirectory called tasks, and start with this test file:

Если вы передадите pytest имя файла, имя каталога или список из них, то будут найдены там вместо текущего каталога. Без аргументов pytest исследует ваш текущий каталог и все подкаталоги для тестовых файлов и запустит тестовый код, который найдёт. Каждый каталог, указанный в командной строке, рекурсивно исследуется для поиска тестового кода.

Для примера, давайте создадим подкаталог, называемый задачами, и начнем с этого тестового файла:

ch1/tasks/test_three.py

"""Проверим тип данных Task.""" from collections import namedtuple
Task = namedtuple('Task', ['summary', 'owner', 'done', 'id'])
Task.__new__.__defaults__ = (None, None, False, None) def test_defaults(): """Использование параметров не должно вызывать значения по умолчанию.""" t1 = Task() t2 = Task(None, None, False, None) assert t1 == t2 def test_member_access(): """Проверка свойства .field (поля) namedtuple.""" t = Task('buy milk', 'brian') assert t.summary == 'buy milk' assert t.owner == 'brian' assert (t.done, t.id) == (False, None)

The test_member_access() test is to demonstrate how to access members by name nd not by index, which is one of the main reasons to use namedtuples.

Let’s put a couple more tests into a second file to demonstrate the _asdict() and _replace() functionality

Тест test_defaults() предназначен для демонстрации и проверки того, как работают умолчания. Вы можете использовать __new __.__ defaults__ для создания объектов Task без указания всех полей.

Тест test_member_access() должен продемонстрировать, как обращаться к членам по имени nd не по индексу, что является одной из основных причин использования namedtuples.
Давайте добавим еще пару тестов во второй файл, чтобы продемонстрировать функции _asdict() и _replace()

ch1/tasks/test_four.py

"""Тест типа данных Task.""" from collections import namedtuple Task = namedtuple('Task', ['summary', 'owner', 'done', 'id']) Task.__new__.__defaults__ = (None, None, False, None) def test_asdict(): """_asdict() должен возвращать словарь.""" t_task = Task('do something', 'okken', True, 21) t_dict = t_task._asdict() expected = assert t_dict == expected def test_replace(): """должно изменить переданное в fields.""" t_before = Task('finish book', 'brian', False) t_after = t_before._replace(id=10, done=True) t_expected = Task('finish book', 'brian', True, 10) assert t_after == t_expected

Если вы не укажете какие-либо файлы или каталоги, pytest будет искать тесты в текущем рабочем каталоге и подкаталогах. Для запуска pytest у вас есть возможность указать файлы и каталоги. Eсли вы запустите pytest из каталога ch1, без команд, вы проведете тесты для четырёх файлов: Он ищет файлы, начинающиеся с test_ или заканчивающиеся на _test.

$ cd /path/to/code/ch1
$ pytest
===================== test session starts ======================
collected 6 items
test_one.py .
test_two.py F
tasks/test_four.py ..
tasks/test_three.py ..
=========================== FAILURES ===========================
_________________________ test_failing _________________________
def test_failing():
> assert (1, 2, 3) == (3, 2, 1)
E assert (1, 2, 3) == (3, 2, 1)
E At index 0 diff: 1 != 3
E Use -v to get the full diff
test_two.py:2: AssertionError
============== 1 failed, 5 passed in 0.08 seconds ==============

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

$ pytest tasks/test_three.py tasks/test_four.py
===================== test session starts ======================
collected 4 items
tasks/test_three.py ..
tasks/test_four.py ..
=================== 4 passed in 0.02 seconds ===================
$ pytest tasks
===================== test session starts ======================
collected 4 items
tasks/test_four.py ..
tasks/test_three.py ..
=================== 4 passed in 0.03 seconds ===================
$ cd /path/to/code/ch1/tasks
$ pytest
===================== test session starts ======================
collected 4 items
test_four.py ..
test_three.py ..
=================== 4 passed in 0.02 seconds ===================

pytest смог найти все те тесты, которые мы хотели запустить, потому что мы назвали их в соответствии с соглашениями об именах pytest. Часть выполнения pytest, где pytest проходит и находит, какие тесты запускать, называется test discovery (тестовым обнаружением).

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

  • Тестовые файлы должны быть названы test_<something>.py или <something>_test.py.
  • Методы и функции тестирования должны быть названы test_<something>.
  • Тестовые классы должны быть названы Test<Something>.

Есть способы изменить эти правила обнаружения, если у вас есть куча тестов с разными именами.
Я расскажу об этом в главе 6, "конфигурация", на странице 113. Поскольку наши тестовые файлы и функции начинаются с test_, то у нас всё в порядке.

Давайте более подробно рассмотрим результат запуска только одного файла:

$ cd /path/to/code/ch1/tasks
$ pytest test_three.py
================= test session starts ==================
platform darwin -- Python 3.6.2, pytest-3.2.1, py-1.4.34, pluggy-0.4.0
rootdir: /path/to/code/ch1/tasks, inifile:
collected 2 items
test_three.py ..
=============== 2 passed in 0.01 seconds ===============

Результат говорит нам совсем немного.

===== test session starts ====

.

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

На компьютере с ОС Windows платформа отличается. Платформа darwin — у меня на Mac. И py, и pluggy — это пакеты, разработанные командой pytest, чтобы помочь с реализацией pytest. Далее перечислены версии Python и pytest, а также зависимости от пакетов pytest.

rootdir: /path/to/code/ch1/tasks, inifile:

В inifile (здесь пустой) перечислены используемые файлы конфигурации. rootdir — это самый верхний общий каталог для всех каталогов в которых ищется тестовый код. Более подробные сведения о конфигурационных файлах вы найдете в главе 6 «Конфигурация» на стр. Конфигурационными файлами могут быть pytest.ini, tox.ini или setup.cfg. 113.

collected 2 items

Это две тестовые функции в файле.

test_three.py ..

Для каждого тестового файла есть одна строка. test_three.py показывает тестируемый файл. Точки предназначены только для прохождения тестов. Две точки означают, что тесты пройдены — по одной точке для каждой тестовой функции или метода. Если вы хотите видеть больше точек для прохождения тестов, используйте опцию -v или --verbose. Failures (сбоев), errors (ошибок), skips (пропусков), xfails, и xpasses обозначаются с F, E, s, x, и Х, соответственно.

01 seconds == == 2 passed in 0.

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

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

  • PASSED (.): Тест выполнен успешно.
  • FAILED (F): Тест не выполнен успешно (или XPASS + strict).
  • SKIPPED (s): Тест был пропущен. Вы можете заставить pytest пропустить тест, используя декораторы @pytest.mark.skip() или pytest.mark.skipif(), обсуждаемые в разделе пропуск тестов, на стр. 34.
  • xfail (x): Тест не должен был пройти, был запущен и провалился. Вы можете принудительно указать pytest, что тест должен завершиться неудачей, используя декоратор @pytest.mark.xfail(), описанный в маркировках тестов как ожидающий неудачу, на стр. 37.
  • XPASS (X): Тест не должен был пройти, был запущен и прошел!..
  • ERROR (E): Исключение произошло за пределами функции тестирования, либо в фикстуре, обсуждается в главе 3, pytest Фикстуры, на стр. 49, или в hook function, обсуждается в главе 5, Плагины, на странице 95.

Выполнение Только Одного Теста

Укажите файл напрямую и добавьте имя ::test_name: Пожалуй, первое, что вы захотите сделать, после того, как начали писать тесты, — это запустить только один.

$ cd /path/to/code/ch1
$ pytest -v tasks/test_four.py::test_asdict
=================== test session starts ===================
collected 3 items
tasks/test_four.py::test_asdict PASSED
================ 1 passed in 0.01 seconds =================

Теперь давайте рассмотрим некоторые варианты.

Использование Опций

Мы не собираемся использовать их все в этой книге, только некоторые. Мы уже пару раз использовали опцию verbose, -v или --verbose, но есть еще много опций, о которых стоит знать. Ознакомиться с полным списком вы можете с помощью опции pytest --help.

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

$ pytest --help usage: pytest [options] [file_or_dir] [file_or_dir] [...] ... subset of the list ... positional arguments: file_or_dir general: -k EXPRESSION only run tests which match the given substring expression. An expression is a python evaluatable expression where all names are substring-matched against test names and their parent classes. Example: -k 'test_method or test_other' matches all test functions and classes whose name contains 'test_method' or 'test_other', while -k 'not test_method' matches those that don't contain 'test_method' in their names. Additionally keywords are matched to classes and functions containing extra names in their 'extra_keyword_matches' set, as well as functions which have names assigned directly to them. -m MARKEXPR only run tests matching given mark expression. example: -m 'mark1 and not mark2'. --markers show markers (builtin, plugin and per-project ones). -x, --exitfirst exit instantly on first error or failed test. --maxfail=num exit after first num failures or errors. ... --capture=method per-test capturing method: one of fd|sys|no. -s shortcut for --capture=no. ... --lf, --last-failed rerun only the tests that failed at the last run (or all if none failed) --ff, --failed-first run all tests but run the last failures first. This may re-order tests and thus lead to repeated fixture setup/teardown ... reporting: -v, --verbose increase verbosity. -q, --quiet decrease verbosity. --verbosity=VERBOSE set verbosity ... -l, --showlocals show locals in tracebacks (disabled by default). --tb=style traceback print mode (auto/long/short/line/native/no). ... --durations=N show N slowest setup/test durations (N=0 for all). ... collection: --collect-only only collect tests, don't execute them. ... test session debugging and configuration: --basetemp=dir base temporary directory for this test run.(warning: this directory is removed if it exists) --version display pytest lib version and import information. -h, --help show help message and configuration info

--collect-only

Этот параметр удобно сначала показать, чтобы выходные данные можно было использовать в качестве ссылки для остальных примеров. Параметр --collect-only показывает, какие тесты будут выполняться с заданными параметрами и конфигурацией. Если вы начинаете в каталоге ch1, вы должны увидеть все тестовые функции, которые вы смотрели до сих пор в этой главе:

$ cd /path/to/code/ch1
$ pytest --collect-only =================== test session starts ===================
collected 6 items
<Module 'test_one.py'> <Function 'test_passing'>
<Module 'test_two.py'> <Function 'test_failing'>
<Module 'tasks/test_four.py'> <Function 'test_asdict'> <Function 'test_replace'>
<Module 'tasks/test_three.py'> <Function 'test_defaults'> <Function 'test_member_access'> ============== no tests ran in 0.03 seconds ===============

Мы будем использовать его снова с -k, чтобы показать, как это работает. Параметр --collect-only полезен для проверки правильности выбора других опций, которые выбирают тесты перед запуском тестов.

-k EXPRESSION

Параметр -k позволяет использовать выражение для определения функций тестирования.

Её можно использовать как ярлык для запуска отдельного теста, если имя уникально, или запустить набора тестов, которые имеют общий префикс или суффикс в их именах. Весьма мощная опция! Вы можете проверить фильтр с помощью: --collect-only: Предположим, вы хотите запустить тесты test_asdict() и test_defaults().

$ cd /path/to/code/ch1
$ pytest -k "asdict or defaults" --collect-only =================== test session starts ===================
collected 6 items
<Module 'tasks/test_four.py'> <Function 'test_asdict'>
<Module 'tasks/test_three.py'> <Function 'test_defaults'> =================== 4 tests deselected ====================
============== 4 deselected in 0.03 seconds ===============

Это похоже на то, что нам надо. Ага! Теперь вы можете запустить их, удалив --collect-only:

$ pytest -k "asdict or defaults"
=================== test session starts ===================
collected 6 items
tasks/test_four.py .
tasks/test_three.py . =================== 4 tests deselected ====================
========= 2 passed, 4 deselected in 0.03 seconds ==========

Просто точки. Упс! Но были ли они правильными тестами? Так значит они прошли. Один из способов узнать — использовать -v или --verbose:

$ pytest -v -k "asdict or defaults" =================== test session starts ===================
collected 6 items
tasks/test_four.py::test_asdict PASSED
tasks/test_three.py::test_defaults PASSED =================== 4 tests deselected ====================
========= 2 passed, 4 deselected in 0.02 seconds ==========

Это были правильные тесты. Ага!

-m MARKEXPR

В качестве примера, один из способов запустить test_replace() и test_member_access(), даже если они находятся в отдельных файлах, это пометить их. Маркеры-один из лучших способов пометить подмножество тестовых функций для совместного запуска. Допустим, вы хотите использовать run_these_please. Можно использовать любое имя маркера. Отметим тесты, используя декоратор @pytest.mark.run_these_please, вот так:

import pytest
...
@pytest.mark.run_these_please
def test_member_access():
...

Затем вы можете запустить все тесты с тем же маркером с помощью pytest -m run_these_please: Теперь то же самое для test_replace().

$ cd /path/to/code/ch1/tasks
$ pytest -v -m run_these_please ================== test session starts ===================
collected 4 items
test_four.py::test_replace PASSED
test_three.py::test_member_access PASSED =================== 2 tests deselected ===================
========= 2 passed, 2 deselected in 0.02 seconds =========

Вы можете использовать такие варианты, как -m "mark1 and mark2" для тестов с обоими маркерами, -m "mark1 and not mark2" для тестов, которые имеют метку 1, но не метку 2, -m "mark1 or mark2" для тестов с одним из и т. Выражение маркера не обязательно должно быть одним маркером. 31. д., Я более подробно обсужу маркеры в Методах проверки маркировки, на стр.

-x, --exitfirst

Если тестовая функция обнаружит сбой assert или exception, выполнение этого теста останавливается, и тест завершается ошибкой. Нормальным поведением pytest является запустить все тесты, которые он найдет. По большей части, это то что надо. И тогда pytest запускает следующий тест. Вот что делает -x опция. Однако, особенно при отладке проблемы, мешает сразу всей тестовой сессии, когда тест не является правильным. Давайте попробуем это на шести тестах, которые у нас есть в настоящий момент:

$ cd /path/to/code/ch1
$ pytest -x ====================== test session starts ====================
collected 6 items
test_one.py .
test_two.py F ============================ FAILURES =========================
__________________________ test_failing _______________________ def test_failing():
> assert (1, 2, 3) == (3, 2, 1)
E assert (1, 2, 3) == (3, 2, 1)
E At index 0 diff: 1 != 3
E Use -v to get the full diff test_two.py:2: AssertionError
=============== 1 failed, 1 passed in 0.38 seconds ============

Без -x все шесть тестов были бы запущены. В верхней части выходных данных вы видите, что все шесть тестов (или “элементов (items)”) были собраны, а в нижней строке вы видите, что один тест не прошел и один прошел, и pytest отобразил строку "прервано (Interrupted)", чтобы сообщить нам, что он остановлен. А также используем --tb=no, чтобы отключить трассировку стека, так как вы ее уже видели и не обязательно видеть её снова: Давайте повторим еще раз без-x.

$ cd /path/to/code/ch1
$ pytest --tb=no =================== test session starts ===================
collected 6 items test_one.py .
test_two.py F
tasks/test_four.py ..
tasks/test_three.py .. =========== 1 failed, 5 passed in 0.09 seconds ============

Этот пример демонстрирует, что без -x, pytest отмечает сбой в test_two.py и продолжает дальнейшее тестирование.

--maxfail=num

Если вы хотите, чтобы некоторые число сбоев было допущено, но не целая тонна, используйте параметр --maxfail, чтобы указать, сколько ошибок допускается получить. Параметр -x приводит к остановке после первого отказа теста. Поскольку есть только один сбой, если мы установили --maxfail = 2, все тесты должны выполняться, а --maxfail = 1 должен действовать так же, как -x: Пока трудно показать это только с одним неудачным тестом в нашей системе, но давайте посмотрим в любом случае.

$ cd /path/to/code/ch1
$ pytest --maxfail=2 --tb=no =================== test session starts ===================
collected 6 items
test_one.py .
test_two.py F
tasks/test_four.py ..
tasks/test_three.py .. =========== 1 failed, 5 passed in 0.08 seconds ============ $ pytest --maxfail=1 --tb=no =================== test session starts ===================
collected 6 items
test_one.py .
test_two.py F
!!!!!!!!! Interrupted: stopping after 1 failures !!!!!!!!!! =========== 1 failed, 1 passed in 0.19 seconds ============

Eще раз мы использовали --tb=no, чтобы отключить трассировку.

-s and --capture=method

Это сокращенный вариант для --capture=no. Флаг -s позволяет печатать операторы — или любой другой вывод, который обычно печатается в stdout, чтобы фактически быть напечатаным в стандартном выводе во время выполнения тестов. Неудачные тесты будут выводиться после того, как тест будет протекать в предположении, что выход поможет вам понять, что что то пошло не так. Смысл в том, что обычно выходные данные захватываются во всех тестах. При разработке тестов я обычно добавляю несколько операторов print(), чтобы можно было следить за ходом теста. Параметр -s или --capture=no отключает захват выходных данных.

Другой вариант, который может помочь вам обойтись без операторов печати в вашем коде,-l/--showlocals, который распечатывает локальные переменные в тесте, если тест терпит неудачу.

— Опция --capture=sys заменяет sys.stdout/stderr в mem-файлах. Другие опции метода захвата --capture=fd и --capture=sys. Опция --capture=fd указывает файловые дескрипторы 1 и 2 на временный файл.

Но, честно говоря, я никогда не нуждался и не использовал их. Я включаю описания sys и fd для полноты. И чтобы полностью описать, как работает -s, мне нужно было коснуться методов захвата. Я часто использую -s.

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

-lf, --last-failed

Просто используйте --lf и вы готовы к отладке: При сбое одного или нескольких тестов способ выполнения только неудачных тестов полезен для отладки.

Это удобно, если вы используете опцию --tb, которая скрывает некоторую информацию, и вы хотите повторить сбои с другой опцией отслеживания.

$ cd /path/to/code/ch1
$ pytest --lf =================== test session starts ===================
run-last-failure: rerun last 1 failures
collected 6 items
test_two.py F ======================== FAILURES =========================
______________________ test_failing _______________________ def test_failing():
> assert (1, 2, 3) == (3, 2, 1)
E assert (1, 2, 3) == (3, 2, 1)
E At index 0 diff: 1 != 3
E Use -v to get the full diff test_two.py:2: AssertionError
=================== 5 tests deselected ====================
========= 1 failed, 5 deselected in 0.08 seconds ==========

–ff, --failed-first

Параметр --ff/--failed-first будет делать то же самое, что и --last-failed, а затем выполнять остальные тесты, прошедшие в прошлый раз:

$ cd /path/to/code/ch1
$ pytest --ff --tb=no =================== test session starts ===================
run-last-failure: rerun last 1 failures first collected 6 items test_two.py F
test_one.py .
tasks/test_four.py ..
tasks/test_three.py .. =========== 1 failed, 5 passed in 0.09 seconds ============

Однако, поскольку test_failing() не удалось в последний раз, --ff заставляет его запускаться в первую очередь Обычно test_failing() из test\_two.py запускается после test\_one.py.

-v, --verbose

Наиболее очевидным отличием является то, что каждый тест получает свою собственную строку, а имя теста и результат прописываются вместо точки. Опция -v/--verbose предоставляет более развернутую информацию по итогам.

Мы уже использовали его пару раз, но давайте запустим его снова для удовольствия в сочетании с --ff и --tb=no:

$ cd /path/to/code/ch1
$ pytest -v --ff --tb=no =================== test session starts ===================
run-last-failure: rerun last 1 failures first
collected 6 items test_two.py::test_failing FAILED
test_one.py::test_passing PASSED
tasks/test_four.py::test_asdict PASSED
tasks/test_four.py::test_replace PASSED
tasks/test_three.py::test_defaults PASSED
tasks/test_three.py::test_member_access PASSED =========== 1 failed, 5 passed in 0.07 seconds ============

На цветном терминале в отчете вы также увидите красный результат FAILED и зеленые PASSED.

-q, --quiet

Мне нравится использовать его в сочетании с --tb=line, в этом случае выводятся только неудавшиеся строки любых неудачных тестов. Опция -q/--quiet противоположна -v/--verbose; она сокращает объем информации в отчете.

Попробуем -q самостоятельно:

$ cd /path/to/code/ch1
$ pytest -q
.F....
======================== FAILURES =========================
______________________ test_failing _______________________ def test_failing():
> assert (1, 2, 3) == (3, 2, 1)
E assert (1, 2, 3) == (3, 2, 1)
E At index 0 diff: 1 != 3
E Full diff:
E - (1, 2, 3)
E ? ^ ^
E + (3, 2, 1)
E ? ^ ^ test_two.py:2: AssertionError
1 failed, 5 passed in 0.08 seconds

Мы будем часто использовать опцию -q в остальной части книги (а также --tb=no), чтобы ограничить вывод тем, что мы конкретно пытаемся понять в то время. Опция -q делает вывод довольно кратким, но обычно этого достаточно.

-l, --showlocals

При использовании параметра -l/--showlocals локальные переменные и их значения отображаются вместе с tracebacks для неудачных тестов.

Если я возьму тест test_replace() и изменю До сих пор у нас не было неудачных тестов с локальными переменными.

t_expected = Task('finish book', 'brian', True, 10)

на

t_expected = Task('finish book', 'brian', True, 11)

Любое изменение ожидаемого значения приведет к сбою. 10 и 11 должны вызвать сбой. Но этого достаточно для демонстрации опции командной строки --l/--showlocals:

$ cd /path/to/code/ch1
$ pytest -l tasks =================== test session starts ===================
collected 4 items
tasks/test_four.py .F
tasks/test_three.py .. ======================== FAILURES =========================
______________________ test_replace _______________________ @pytest.mark.run_these_please def test_replace(): """replace() should change passed in fields.""" t_before = Task('finish book', 'brian', False) t_after = t_before._replace(id=10, done=True) t_expected = Task('finish book', 'brian', True, 11)
> assert t_after == t_expected
E AssertionError: assert Task(summary=...e=True, id=10) == Task(summary='...e=True, id=11)
E At index 3 diff: 10 != 11
E Use -v to get the full diff t_after = Task(summary='finish book', owner='brian', done=True, id=10)
t_before = Task(summary='finish book', owner='brian', done=False, id=None)
t_expected = Task(summary='finish book', owner='brian', done=True, id=11) tasks\test_four.py:28: AssertionError
=========== 1 failed, 3 passed in 0.08 seconds ============

Локальные переменные t_after, t_before и t_expected отображаются после фрагмента кода со значением, которое они содержали во время неудавшегося assert-а.

--tb=style

При сбое теста pytest отображает список сбоев и так называемую обратную трассировку, которая показывает точную строку, в которой произошел сбой. Параметр --tb=style изменяет способ вывода пакетов трассировки для сбоев. Вот где опция--tb=style пригодится. Хотя tracebacks полезны большую часть времени, бывают случаи, когда они раздражают. short печатает только строку assert и символ E без контекста; line сохраняет ошибку в одной строке; no полностью удаляет трассировку. Стили, которые я считаю полезными, это short, line и no.

--tb=no полностью удаляет трассировку Давайте оставим пока модификацию test_replace(), чтобы она завершилась неудачей, и запустим ее с разными стилями трассировки.

$ cd /path/to/code/ch1
$ pytest --tb=no tasks
=================== test session starts ===================
collected 4 items
tasks/test_four.py .F
tasks/test_three.py ..
=========== 1 failed, 3 passed in 0.04 seconds ============

If you have a ton of failing tests, this option can help to show a pattern in the failures: --tb=line in many cases is enough to tell what’s wrong.

Если у вас гора неудачных тестов, этот параметр может помочь отобразить шаблон в сбоях: --tb=line во многих случаях достаточно, чтобы показать, что не так.

$ pytest --tb=line tasks
=================== test session starts ===================
collected 4 items
tasks/test_four.py .F
tasks/test_three.py ..
======================== FAILURES =========================
/path/to/code/ch1/tasks/test_four.py:20:
AssertionError: assert Task(summary=...e=True, id=10) == Task(
summary='...e=True, id=11)
=========== 1 failed, 3 passed in 0.05 seconds ============

Следующий шаг в verbose tracebacks --tb=short:

$ pytest --tb=short tasks
=================== test session starts ===================
collected 4 items
tasks/test_four.py .F
tasks/test_three.py ..
======================== FAILURES =========================
______________________ test_replace _______________________
tasks/test_four.py:20: in test_replace
assert t_after == t_expected
E AssertionError: assert Task(summary=...e=True, id=10) == Task(
summary='...e=True, id=11)
E At index 3 diff: 10 != 11
E Use -v to get the full diff
=========== 1 failed, 3 passed in 0.04 seconds ============

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

Есть три оставшихся варианта трассировки, которые мы пока не рассмотрели.

pytest --tb=auto покажет вам длинную версию для первого и последнего tracebacks, если у вас есть несколько сбоев. pytest --tb=long покажет вам наиболее исчерпывающий и информативный traceback. pytest --tb=native покажет вам стандартную библиотеку traceback без дополнительной информации. Это поведение по умолчанию.

--durations=N

Она не меняет ваши тесты; сообщает самый медленный N номер tests/setups/teardowns по окончании тестов. Опция --durations=N невероятно полезна, когда вы пытаетесь ускорить свой набор тестов. Если вы передадите --durations=0, он сообщит обо всем в порядке от самого медленного к самому быстрому.

1) к одному из тестов. Поскольку наши тесты не достаточно длинные, я добавлю time.sleep(0. Угадайте, какой:

$ cd /path/to/code/ch1
$ pytest --durations=3 tasks
================= test session starts =================
collected 4 items
tasks/test_four.py ..
tasks/test_three.py ..
============== slowest 3 test durations ===============
0.10s call tasks/test_four.py::test_replace
0.00s setup tasks/test_three.py::test_defaults
0.00s teardown tasks/test_three.py::test_member_access
============== 4 passed in 0.13 seconds

Каждый тест по существу состоит из трех этапов: call(вызов), настройки(setup) и опровержения(teardown). Медленный тест с дополнительным sleep появляется сразу же после вызова метки, за которым следует установка и опровержение. Я подробно освещаю фикстуры в главе 3, pytest Fixtures, на стр. Установка и опровержение также являются фикстурой, и вы можете добавить код для получения данных или тестируемой системы программного обеспечения в состояние предварительного условия до запуска теста, а также, при необходимости, очистить их. 49.

--version

Опция --version показывает версию pytest и каталог, в котором он установлен:

$ pytest --version
This is pytest version 3.0.7, imported from
/path/to/venv/lib/python3.5/site-packages/pytest.py

Поскольку мы установили pytest в виртуальную среду, pytest будет находиться в каталоге site-packages этой виртуальной среды.

-h, --help

Она не только показывает вам, как использовать stock-овый pytest, но также расширяется по мере установки плагинов, чтобы показать параметры и переменные конфигурации, добавленные плагинами. Опция -h/--help весьма полезна, даже после того, как вы привыкнете к pytest.

Опция -h показывает:

  • Использование: pytest [опции] [file_or_dir] [file_or_dir] [...]
  • Параметры командной строки и краткое описание, включая добавленные параметры через плагины
  • Список опций, доступных для ini файлов конфигурации стиля, которые я буду обсуждать подробнее в главе 6, Конфигурация, на стр. 113
  • Список переменных среды, которые могут влиять на поведение pytest (также обсуждается в главе 6, Конфигурация, на стр. 113)
  • Напоминание о том, что pytest --markers можно использовать для просмотра доступных маркеров, обсуждаемых в главе 2, Написание тестовых функций, на стр. 23
  • Напоминание о том, что pytest --fixtures могут быть использованы для просмотра доступных фикстур, обсуждаемых в главе 3, Фикстуры pytest, на стр. 49

Последний часть информации текста справки отображает это примечание:

(shown according to specified file_or_dir or current dir if not specified)

Это происходит потому, что по пути к указанному файлу или каталогу pytest может найти файлы conftest.py, которые могут включать функции-ловушки (hook functions), создающие новые параметры, определения фикстур и определения маркеров. Это примечание важно, поскольку параметры, маркеры и фикстуры могут изменяться в зависимости от каталога или тестового файла.

Вы узнаете о файлах conftest.py и ini, таких как pytest.ini в главе 6 «Конфигурация», на странице 113. Возможность настраивать поведение pytest в файлах conftest.py и тестовых файлах позволяет настраивать поведение локально для проекта или даже подмножество тестов для проекта.

Упражнения

  1. Даже если вы знаете, что вам не нужны виртуальные среды для проекта, над которым вы работаете, порадуйте меня и достаточно узнайте о них, чтобы создать виртуалку, дабы опробовать вещи из этой книги. Создайте новую виртуальную среду, используя python -m virtualenv или python -m venv. Прочтите Приложение 1, Виртуальная среда, на стр. Я сопротивлялся их использованию очень долго, и теперь я всегда использую их. 155, если у вас возникли трудности.

  2. Практикуйте активацию и деактивацию виртуальной среды несколько раз.

    - $ source venv/bin/activate
    - $ deactivate On Windows: - C:\Users\okken\sandbox>venv\scripts\activate.bat
    - C:\Users\okken\sandbox>deactivate

  3. См. Установите pytest в новой виртуальной среде. Даже если вы думали, что у вас уже установлен pytest, вам нужно установить его в виртуальную среду, которую вы только что создали. Приложение 2, pip, на странице 159, если у вас возникли проблемы.

  4. Вы можете использовать те, которые мы использовали в этой главе или сделать свои собственные. Создайте несколько тестовых файлов. Попрактикуйте pytest на этих файлах.

  5. Не просто используйте assert something == something_else; попробуйте такие вещи, как: Измените операторы assert.

    • assert 1 in [2, 3, 4]
    • assert a < b
    • assert 'fizz' not in 'fizzbuzz'

Что дальше

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

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

Показать больше

Похожие публикации

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

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

Кнопка «Наверх»