Как выполнять доктесты

По умолчанию все файлы, соответствующие шаблону test*.txt, будут запускаться через стандартный модуль python doctest. Вы можете изменить шаблон, выдав:

pytest --doctest-glob="*.rst"

в командной строке. --doctest-glob может быть задано несколько раз в командной строке.

Если у вас есть текстовый файл следующего вида:

# content of test_example.txt

hello this is a doctest
>>> x = 3
>>> x
3

тогда вы можете просто вызвать pytest напрямую:

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item

test_example.txt .                                                   [100%]

============================ 1 passed in 0.12s =============================

По умолчанию pytest будет собирать test*.txt файлов в поисках директив doctest, но вы можете передать дополнительные глобы, используя опцию --doctest-glob (разрешено несколько).

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

# content of mymodule.py
def something():
    """a doctest in a docstring
    >>> something()
    42
    """
    return 42
$ pytest --doctest-modules
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items

mymodule.py .                                                        [ 50%]
test_example.txt .                                                   [100%]

============================ 2 passed in 0.12s =============================

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

# content of pytest.ini
[pytest]
addopts = --doctest-modules

Кодирование

По умолчанию используется кодировка UTF-8, но вы можете указать кодировку, которая будет использоваться для этих файлов doctest, используя опцию doctest_encoding ini>:

# content of pytest.ini
[pytest]
doctest_encoding = latin1

Использование опций „doctest“

Стандартный модуль Python doctest предоставляет некоторые options для настройки строгости тестов doctest. В pytest вы можете включить эти флаги с помощью конфигурационного файла.

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

[pytest]
doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL

В качестве альтернативы, опции можно включить с помощью встроенного комментария в самом тесте doc:

>>> something_that_raises()  # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ValueError: ...

pytest также вводит новые опции:

  • ALLOW_UNICODE: если включено, префикс u удаляется из юникодовых строк в ожидаемом выводе доктестов. Это позволяет запускать доктесты в Python 2 и Python 3 без изменений.

  • ALLOW_BYTES: аналогично, префикс b удаляется из байтовых строк в ожидаемом выводе doctest.

  • NUMBER: если включено, числа с плавающей точкой должны совпадать только в пределах точности, указанной в ожидаемом выводе doctest. Числа сравниваются с помощью pytest.approx() с относительным допуском, равным точности. Например, при сравнении 3.14 с pytest.approx(math.pi, rel=10**-2) в следующем выводе требуется совпадение только до 2 знаков после запятой:

    >>> math.pi
    3.14
    

    Если бы вы написали 3.1416, то фактический вывод должен был бы соответствовать приблизительно 4 знакам после запятой; и так далее.

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

    Expected:
        0.233
    Got:
        0.23300000000000001
    

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

    Добавлено в версии 5.1.

Продолжить при неудаче

По умолчанию pytest сообщает только о первой неудаче для данного doctest. Если вы хотите продолжить тест даже при наличии неудач, сделайте следующее:

pytest --doctest-modules --doctest-continue-on-failure

Формат вывода

Вы можете изменить формат вывода diff при неудаче для ваших доктестов, используя один из стандартных форматов модулей doctest в опциях (см. doctest.REPORT_UDIFF, doctest.REPORT_CDIFF, doctest.REPORT_NDIFF, doctest.REPORT_ONLY_FIRST_FAILURE):

pytest --doctest-modules --doctest-report none
pytest --doctest-modules --doctest-report udiff
pytest --doctest-modules --doctest-report cdiff
pytest --doctest-modules --doctest-report ndiff
pytest --doctest-modules --doctest-report only_first_failure

особенности питекантропа

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

Использование приспособлений

Возможно использование фиксаторов с помощью помощника getfixture:

# content of example.rst
>>> tmp = getfixture('tmp_path')
>>> ...
>>>

Обратите внимание, что приспособление должно быть определено в месте, видимом pytest, например, в файле conftest.py или плагине; обычные файлы python, содержащие docstrings, обычно не проверяются на наличие приспособлений, если они явно не настроены с помощью python_files.

Кроме того, при выполнении текстовых файлов doctest поддерживается метка usefixtures и фиксаторы, помеченные как autouse.

приспособление „doctest_namespace“

Закрепление doctest_namespace можно использовать для внедрения элементов в пространство имен, в котором выполняются ваши тесты. Оно предназначено для использования внутри ваших собственных фикстур, чтобы обеспечить тесты, которые их используют, контекстом.

doctest_namespace - это стандартный объект dict, в который вы помещаете объекты, которые вы хотите видеть в пространстве имен doctest:

# content of conftest.py
import numpy


@pytest.fixture(autouse=True)
def add_np(doctest_namespace):
    doctest_namespace["np"] = numpy

которые затем можно использовать непосредственно в ваших тестах:

# content of numpy.py
def arange():
    """
    >>> a = np.arange(10)
    >>> len(a)
    10
    """

Обратите внимание, что, как и в случае с обычным conftest.py, фикстуры обнаруживаются в дереве каталогов, в котором находится conftest. Это означает, что если вы поместили свой doctest вместе с исходным кодом, соответствующий conftest.py должен находиться в том же дереве каталогов. Фикстуры не будут обнаружены в дереве каталогов сиблингов!

Пропуск тестов

По тем же причинам, по которым можно пропустить обычные тесты, можно также пропустить тесты внутри доктестов.

Чтобы пропустить одну проверку внутри доктеста, можно использовать стандартную директиву doctest.SKIP:

def test_random(y):
    """
    >>> random.random()  # doctest: +SKIP
    0.156231223

    >>> 1 + 1
    2
    """

Это позволит пропустить первую проверку, но не вторую.

pytest также позволяет использовать стандартные функции pytest pytest.skip() и pytest.xfail() внутри доктестов, что может быть полезно, поскольку вы можете пропускать/не выполнять тесты, основываясь на внешних условиях:

>>> import sys, pytest
>>> if sys.platform.startswith('win'):
...     pytest.skip('this doctest does not work on Windows')
...
>>> import fcntl
>>> ...

Однако использовать эти функции не рекомендуется, поскольку это снижает читабельность документальной строки.

Примечание

pytest.skip() и pytest.xfail() ведут себя по-разному в зависимости от того, находятся ли доктесты в файле Python (в docstrings) или в текстовом файле, содержащем доктесты вперемешку с текстом:

  • Модули Python (docstrings): функции действуют только в данной конкретной docstring, позволяя другим docstrings в том же модуле выполняться как обычно.

  • Текстовые файлы: функции будут пропускать/не выполнять проверки для оставшейся части всего файла.

Альтернативы

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

  • pytest-doctestplus: обеспечивает расширенную поддержку doctest и позволяет тестировать файлы reStructuredText («.rst»).

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

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