doctest — Тестовые интерактивные примеры Python

Исходный код: Lib/doctest.py


Модуль doctest выполняет поиск частей текста, которые выглядят как интерактивные сеансы Python, а затем выполняет эти сеансы, чтобы убедиться, что они работают точно так, как показано. Существует несколько распространенных способов использования doctest:

  • Проверка актуальности докстрингов модуля путем проверки того, что все интерактивные примеры по-прежнему работают в соответствии с документацией.
  • Для проведения регрессионного тестирования, проверяя, что интерактивные примеры из тестового файла или тестового объекта работают как положено.
  • Для написания учебной документации по пакету, подробно иллюстрированного примерами ввода-вывода. В зависимости от того, выделены ли примеры или текст изложения, она выглядит как «грамотное тестирование» или «исполняемая документация».

Вот полный, но небольшой модуль примерный:

"""
Это модуль "пример".

Модуль пример предоставляет одну функцию factorial(). Например,

>>> factorial(5)
120
"""

def factorial(n):
    """Вернуть факториал n, целого числа >= 0.

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(30)
    265252859812191058636308480000000
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    ValueError: n must be >= 0

    Factorials of floats are OK, but the float must be an exact integer:
    >>> factorial(30.1)
    Traceback (most recent call last):
        ...
    ValueError: n must be exact integer
    >>> factorial(30.0)
    265252859812191058636308480000000

    It must also not be ridiculously large:
    >>> factorial(1e100)
    Traceback (most recent call last):
        ...
    OverflowError: n too large
    """

    import math
    if not n >= 0:
        raise ValueError("n must be >= 0")
    if math.floor(n) != n:
        raise ValueError("n must be exact integer")
    if n+1 == n:  # поймать значение как 1e300
        raise OverflowError("n too large")
    result = 1
    factor = 2
    while factor <= n:
        result *= factor
        factor += 1
    return result


if __name__ == "__main__":
    import doctest
    doctest.testmod()

Если запустить example.py непосредственно из командной строки, doctest работает как волшебство:

$ python example.py
$

Нет никаких выходных данных! Это нормально, и это означает, что все примеры сработали. Передайте -v к сценарию и doctest напечатает подробный лог того, что он пытается выполнить и печатает в конце резюме:

$ python example.py -v
Trying:
    factorial(5)
Expecting:
    120
ok
Trying:
    [factorial(n) for n in range(6)]
Expecting:
    [1, 1, 2, 6, 24, 120]
ok

И так далее, в конце заканчивая:

Trying:
    factorial(1e100)
Expecting:
    Traceback (most recent call last):
        ...
    OverflowError: n too large
ok
2 items passed all tests:
   1 tests in __main__
   8 tests in __main__.factorial
9 tests in 2 items.
9 passed and 0 failed.
Test passed.
$

Это все, что вам нужно знать, чтобы начать продуктивно использовать doctest! В следующих разделах содержится полная информация. Обратите внимание, что есть много примеров doctests в стандартном наборе тестов Python и библиотеках. Особенно полезные примеры можно найти в стандартном файле тестирования Lib/test/test_doctest.py.

Простое использование: проверка примеров в докстрингах

Самый простой метод начать использовать doctest (рекомендуется, но не обязательно к использованию) - завершать каждый модуль M следующим:

if __name__ == "__main__":
    import doctest
    doctest.testmod()

затем doctest проверяет докстринги в модуле M.

Выполнение модуля в виде сценария приводит к выполнению и проверке примеров в докстринговах:

python M.py

При этом ничего не будет отображаться, если пример не завершится неудачей, и в этом случае пример(ы) отказа и причина(ы) отказа(ов) будут напечатаны в stdout, а завершающая строка вывода будет ***Test Failed*** N failures., где N - количество неудачных примеров.

Запустите модуль с переключателем -v:

python M.py -v

и подробный отчет о всех опробованных примерах напечатается в стандартный вывод вместе с различными резюме в конце.

Вы можете вызвать подробный режим, передав verbose=True в testmod() или отключить его, передав verbose=False. В любом из этих случаев sys.argv не рассматривается testmod() (так что передача -v или нет не принесёт никакого эффекта).

Есть также короткий путь командной строки для управления testmod(). Вы можете дать указание интерпретатору Python управлять doctest модулем непосредственно из стандартной библиотеки и передавать имя(имена) модуля в командной строке:

python -m doctest -v example.py

Эта строка импортирует example.py как автономный модуль и запустит testmod() на нем. Обратите внимание, что это может работать неправильно, если файл является частью пакета и импортирует другие подмодули из этого пакета.

Дополнительные сведения о testmod() см. в разделе Основной API.

Простое использование: проверка примеров в текстовом файле

Другим простым приложением doctest является тестирование интерактивных примеров в текстовом файле. Это можно сделать с помощью функции testfile():

import doctest
doctest.testfile("example.txt")

Этот короткий сценарий выполняет и проверяет все интерактивные примеры Python, содержащиеся в файле example.txt. Содержимое файла рассматривается как единый гигантский докстринг; файл не должен содержать программу Python! Возможный пример example.txt содержащий следующее:

Модуль ``example``
===================

Использование ``factorial``
----------------------------

Это пример текстового файла в формате reStructuredText. Сначала импортируйте
``factorial`` из модуля ``example``:

    >>> from example import factorial

Теперь выполним его:

    >>> factorial(6)
    120

Запуск doctest.testfile("example.txt") сообщит о найденной ошибке в документации:

File "./example.txt", line 14, in example.txt
Failed example:
    factorial(6)
Expected:
    120
Got:
    720

Как и в случае с testmod(), testfile() ничего не покажет, если пример провалился. Если пример завершается неуспешно, то пример(ы) отказа и причина(ы) отказа(ов) печатаются в stdout с использованием того же формата, что и testmod().

По умолчанию функция testfile() выполняет поиск файлов в каталоге вызывающего модуля. Посмотрите раздел Основной API для описания дополнительных аргументов, которые могут быть используемы, чтобы сказать ему где искать файлы в других местоположениях.

Как и testmod(), подробность testfile() может быть установлена с помощью переключателя командной строки -v или с дополнительным ключевым аргументом verbose.

Есть также короткий путь командной строки для управления testfile(). Вы можете дать указание интерпретатору Python запустить модуль doctest непосредственно из стандартной библиотеки и передавать имя(имена) файла(ов) в командной строке:

python -m doctest -v example.txt

Поскольку имя файла не заканчивается на .py, doctest делает вывод, что он должен выполняться с testfile(), а не testmod().

Дополнительные сведения о testfile() см. в разделе Основной API.

Как это работает

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

Какие докстринги исследуются?

Модуль docstring и все его функции, класс и метод докстрингов являются поисковыми. Объекты, импортированные в модуль, не являются поисковыми.

Кроме того, если M.__test__ существует и содержит значение true, он должен быть словарем, и каждая запись сопоставляет имени (строки) объекту функции, объекту класса или строки. Функция и объект класса докстрингов, найденные в M.__test__ подвергаются просмотру, и строки рассматриваются как докстринги. В выходных данных появляется ключ K в M.__test__ с именем:

<name of M>.__test__.K

Любые найденные классы рекурсивно просмотренные аналогично тестированию докстрингами, содержащихся в них методах и вложенных классах.

Детали реализации CPython: До версии 3.4 модули расширений, написанные на языке C, не полностью просматривались doctest.

Как распознаются примеры докстринга?

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

>>> # комментарии игнорируются
>>> x = 12
>>> x
12
>>> if x == 13:
...     print("yes")
... else:
...     print("no")
...     print("NO")
...     print("NO!!!")
...
no
NO
NO!!!
>>>

Любой ожидаемый выход должен немедленно следовать за финишной строкой '>>> ' или '... ', содержащей код и ожидаемый выход (если имеется) распространяется на следующую строку '>>> ' или строку с одними пробелами.

Мелким шрифтом:

  • Ожидаемый выход не может содержать полностью пустую строку, т.к. такая строка используется для сигнализации конца ожидаемого выхода. Если ожидаемый вывод содержит пустую строку, поместите в каждое место <BLANKLINE> в свой doctest код, где ожидается пустая строка.

  • Все жесткие символы табуляции разворачиваются до пробелов, используя 8-колоночные позиции табуляции. Табы в выходных данных, генерируемых тестирующим кодом, не изменяются. Поскольку любые жесткие табы в примере вывода разворачиваются, это означает, если вывод кода включает жесткие табы, единственный метод пройти doctest, если передать опцию NORMALIZE_WHITESPACE или директиву в реальный код. Альтернативно, тест может быть перезаписан для захватом вывода и сравнения его с ожидаемым значением в рамках теста. Эта обработка табов в источнике была достигнута путем проб и ошибок и оказалась наименее подверженным ошибкам способом их обработки. Для обработки табов можно использовать другой алгоритм, записывая пользовательский класс DocTestParser.

  • Вывод в stdout захватывается, но не в stderr (исключение, трейсбэки захватываются через другое средство).

  • В противном случае обратная косая черта будет интерпретирована как часть строки. Например, \n выше будет интерпретироваться как символ новой строки. В качестве альтернативы, вы можно удвоить каждую обратную косую черту в версии doctest (и не использовать необработанную строку):

    >>> def f(x):
    ...     r'''Backslashes in a raw docstring: m\n'''
    >>> print(f.__doc__)
    Backslashes in a raw docstring: m\n
    
  • Начальный столбец не имеет значения:

    >>> assert "Easy!"
          >>> import math
              >>> math.floor(1.9)
              1
    

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

Каков контекст выполнения?

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

Вы можете принудительно использовать свои собственные словарь в качестве контекста выполнения, передав globs=your_dict в testmod() или testfile() вместо него.

Как насчет исключений?

Нет проблем, при условии, что трейсбэк является единственным выводом, производимым примером: просто помещая в трейсбэк. [1] Поскольку трейсбэки содержат сведения, которые быстро меняются (например, точные пути к файлам и номера строк), это один из случаев, когда doctest упорно работает, чтобы быть гибким в том, что он принимает.

Простой пример:

>>> [1, 2, 3].remove(42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list

Этот тест завершается успешно, если поднят ValueError, с отображением детализации list.remove(x): x not in list.

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

Traceback (most recent call last):
Traceback (innermost last):

За заголовком трейсбэк следует необязательный стек трейсбэка, содержимое которого игнорируется doctest. Стек трейсбэка обычно пропускается или дословно копируется из интерактивного сеанса.

За стеком трейсбэк следует наиболее интересная часть: строки, содержащие тип исключения и подробности. Обычно это последняя строка трейсбэка, но может распространяться на несколько строк, если исключение содержит многострочные подробности:

>>> raise ValueError('multi\n    line\ndetail')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: multi
    line
detail

Последние три строки (начиная с ValueError) сравниваются с типом и подробностями исключения, а остальные игнорируются.

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

>>> raise ValueError('multi\n    line\ndetail')
Traceback (most recent call last):
    ...
ValueError: multi
    line
detail

Отметим, что к трейсбэки обрабатываются очень специфически. В частности, в переписанном примере использование ... не зависит от параметра doctest’а ELLIPSIS. Многоточие в этом примере может быть пропущено, или с тем же успехом может быть три(или триста) запятых или цифр или фрагмента стенограммы скетча Монти Пайтон.

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

  • Doctest не может угадать, был ли ожидаемый вывод получен из исключения трейсбэк или из обычной печати. Так пример, который ожидает передачи ValueError: 42 is prime, на самом деле получит ValueError или если пример просто печатает это текст трейсбэка. На практике обычный вывод редко начинается с строки заголовка трейсбэка, так что это не создает реальных проблем.
  • У каждой строки стека трейсбэка (если есть) должен быть остступ больше, чем у первой строки примера, или начинаются с неалфавитно-цифрового символа. Первая строка после заголовка трассировки содержит тот же отступ и начинается с алфавитно-цифровым началом предоставленной детализации исключения. Конечно, это делает правильные вещи для подлинных трейсбэков.
  • Когда определена опция IGNORE_EXCEPTION_DETAIL doctest, все после крайнего левого двоеточия и любой информации о модуле в имени исключения игнорируется.
  • Интерактивная оболочка пропускает строку заголовка трейсбэк для некоторых SyntaxErrorов. Но doctest использует строку заголовка трейсбэк, чтобы отличать исключения от неисключений. Поэтому в редких случаях, когда необходимо протестировать SyntaxError, в котором отсутствует заголовок трейсбэка, необходимо вручную добавить строку заголовка трейсбэка в тестовый пример.
  • Для некоторого SyntaxError Python показывает позицию символа синтаксической ошибки, используя маркер ^:

    >>> 1 1
      File "<stdin>", line 1
        1 1
          ^
    SyntaxError: invalid syntax
    

    Поскольку строки, показывающие положение ошибки, находятся перед типом особой ситуации и подробностями, они не проверяются доктестом. Например, следующий тест будет пройден, даже если он поместит маркер ^ в неправильное место:

    >>> 1 1
      File "<stdin>", line 1
        1 1
        ^
    SyntaxError: invalid syntax
    

Флаги выбора

Ряд флагов опций управляет различными аспектами поведения doctest. Символические названия флагов поставляются как константы модуля, которые могут быть вместе побитными или и переданны в различные функции. Имена также могут быть используемый в директивах doctest и могут быть переданы в интерфейс командной строки doctest через опцию -o.

Добавлено в версии 3.4: Параметр командной строки -o.

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

doctest.DONT_ACCEPT_TRUE_FOR_1

По умолчанию, если ожидаемый блок вывода содержит только 1, фактический блок вывода, содержащий только 1 или True, считается совпадением, аналогично для 0 против False. При указании параметра DONT_ACCEPT_TRUE_FOR_1 не допускается ни одна из замен. Поведение по умолчанию склоняет Python к изменению типов многих возвращаемых функций от целого числа до булева; в данных случаях все ещё работают доктесты, ожидающие «маленькие целочисленные» выходные данные все еще работают. Этот вариант, вероятно, исчезнет, но не на несколько лет.

doctest.DONT_ACCEPT_BLANKLINE

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

doctest.NORMALIZE_WHITESPACE

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

doctest.ELLIPSIS

Если указан маркер эллипсиса (...) в ожидаемом выводе может соответствовать любой подстроке в фактическом выводе. Он включает подстроки, которые охватывают границы строк, и пустые подстроки, поэтому лучше всего использовать его по минимуму. Сложное использование может привести к одним и тем же видам «упс, он слишком много соответствовал!» сюрпризов, к которым .* склонен в регулярных выражениях.

doctest.IGNORE_EXCEPTION_DETAIL

Если указано, наример ожидаемое исключение проходит в случае возникновения исключения ожидаемого типа, даже если детали исключения не совпадают. Например, ожидаемое ValueError: 42 пройдёт, если фактическое вызванное исключение равно ValueError: 3*14, но потерпит неудачу, при возникновении TypeError.

Он также игнорирует имя модуля используемый в отчетах doctest Python 3. Следовательно, оба этих варианта будут работать с указанным флагом, независимо от этого:

>>> raise CustomError('message')
Traceback (most recent call last):
CustomError: message

>>> raise CustomError('message')
Traceback (most recent call last):
my_module.CustomError: message

Обратите внимание, что ELLIPSIS может также применяться для игнорирования подробностей сообщения об исключении, но такой тест все равно может потерпеть неудачу на основе того, напечатаны ли детали модуля как часть имени исключения. Использование IGNORE_EXCEPTION_DETAIL и подробностей из Python 2.3 также является единственным понятным способом написать доктест, который не заботится о деталях исключения, но продолжает проходить под Python 2.3 или ранее (эти выпуски не поддерживают doctest директивы и игнорируют их как неактуальные комментарии). Например:

>>> (1, 2)[3] = 'moo'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object doesn't support item assignment

проходит для Python 2.3 и более поздних Python версий с указанным флагом, даже несмотря на то, что подробности изменения в Python 2.4 говорят «does not» вместо «doesn’t».

Изменено в версии 3.2: IGNORE_EXCEPTION_DETAIL теперь также игнорирует любую информацию, относящуюся к модулю, содержащему тестируемое исключение.

doctest.SKIP

Если указано, не запускать пример вообще. Это может быть полезно в контекстах, где примеры doctest служат как документацией, так и контрольными примерами, и пример должен быть включен для целей документации, но не должен проверяться. Например, выходные данные примера могут быть случайными; или пример может зависеть от ресурсов, которые будут недоступны для тестов.

Флаг SKIP может также быть используемый для того, чтобы временно «прокомментировать» примеры.

doctest.COMPARISON_FLAGS

Битовая маска «или» для объединение всех вышеуказанных флагов сравнения.

Вторая группа параметров управляет тем, как регистрируются сбои тестирования:

doctest.REPORT_UDIFF

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

doctest.REPORT_CDIFF

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

doctest.REPORT_NDIFF

Если указано, различия вычисляются методом difflib.Differ, используя тот же алгоритм, что и популярная утилита ndiff.py. Это единственный метод, который маркирует различия как внутри строк, так и между ними. Например, если строка ожидаемого вывода содержит цифру 1, где фактический вывод содержит букву l, то вставляется строка с курсором, обозначающим несоответствующие позиции столбца.

doctest.REPORT_ONLY_FIRST_FAILURE

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

doctest.FAIL_FAST

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

В командной строке doctest параметр -f принимается как сокращение для -o FAIL_FAST.

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

doctest.REPORTING_FLAGS

Битовая маска «или» объединяющая все флаги отчетности выше.

Существует также метод регистрации новых имен флагов опций, хотя это не так полезно, по сравнению с расширением внутренностей doctest через подклассы:

doctest.register_optionflag(name)

Создать новый флаг опции с заданным именем и вернуть целочисленное значение нового флага. register_optionflag() может быть использован для создания подклассов OutputChecker или DocTestRunner для создания новых опций, поддерживающих вашим подклассы. register_optionflag() всегда следует называть с помощью следующей идиомы:

MY_FLAG = register_optionflag('MY_FLAG')

Директивы

Директивы doctest могут быть использьзованы для модификации флагов опций для отдельных примеров. Директивы doctest - специальные комментарии Python после примера исходного кода:

doctest               ::=  «#» «doctest»: 'directive_options'
directive_options     ::=  'directive_option' («» 'directive_option') \*
directive_option      ::=  'on_or_off' 'directive_option_name' on_or_off: «+» \| «-»
directive_option_name ::=  «DONT_ACCEPT_BLANKLINE» \| «NORMALIZE_WHITESPACE» \|...

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

Директивы doctest примера изменяют поведение doctest для этого примера. Используйте команду +, чтобы включить именованное поведение, или команду -, чтобы отключить его.

Например, этот тест проходит успешно:

>>> print(list(range(20))) 
[0,   1,  2,  3,  4,  5,  6,  7,  8,  9,
10,  11, 12, 13, 14, 15, 16, 17, 18, 19]

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

>>> print(list(range(20))) 
[0, 1, ..., 18, 19]

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

>>> print(list(range(20))) 
...                        
[0,    1, ...,   18,    19]

Если для одного примера используются несколько директив комментариев, то они комбинируются:

>>> print(list(range(20))) 
...                        
[0,    1, ...,   18,    19]

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

>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40)))
... 
[0, ..., 4, 10, ..., 19, 30, ..., 39]

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

Предупреждения

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

>>> foo()
{"Hermione", "Harry"}

это уязвимо! Один обходной путь состоит в том, чтобы сделать вместо

>>> foo() == {"Hermione", "Harry"}
True

выполнить:

>>> d = sorted(foo())
>>> d
['Harry', 'Hermione']

Примечание

До Python 3.6, при печати словаря, Python не гарантировал, что пары ключ-значение были напечатаны в каком-либо конкретном порядке.

Есть и другие примеры, но вы поняли идею.

Другая плохая идея - печатать что-либо, которые встраивают адрес объекта, например:

>>> id(1.0) # наверняка в какой-то момент потерпит неудачу
7948648
>>> class C: pass
>>> C()   # функция repr() по умолчанию для экземпляров вставляет адрес
<__main__.C instance at 0x00AC18F0>

Директива ELLIPSIS предлагает хороший подход для последнего примера:

>>> C() 
<__main__.C instance at 0x...>

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

>>> 1./7  # рискованный
0.14285714285714285
>>> print(1./7) # безопаснее
0.142857142857
>>> print(round(1./7, 6)) # гораздо безопаснее
0.142857

Числа в формы I/2.**J безопасны на всех платформах, и я часто изобретаю примеры доктестов для получения чисел этой формы:

>>> 3./4  # совершенно безопасно
0.75

Простые дроби также легче понять людям и это делает документацию лучше .

Основной API

Функции testmod() и testfile() предоставляют простой интерфейс для doctest, который должен быть достаточным для большинства основных применений. Менее формальное введение в эти две функции см. в разделах Простое использование: проверка примеров в докстрингах и Простое использование: проверка примеров в текстовом файле.

doctest.testfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, parser=DocTestParser(), encoding=None)

Все аргументы, кроме filename, являются необязательными и должны быть указаны в ключевой форме.

Примеры тестов в файле с именем filename. Возвращает (failure_count, test_count).

Необязательный аргумент module_relative указывает, как следует интерпретировать имя файла:

  • Если module_relative содержит значение True (значение по умолчанию), то filename определяет независимый от ОС путь к модулю. По умолчанию этот путь относится к каталогу вызывающего модуля; но если указан аргумент package, то он относится к этому пакету. Для обеспечения независимости ОС, filename должен использовать символы / для разделения сегментов пути и не может быть абсолютным путем (т.е. он может не начинаться с /).
  • Если module_relative является False, то filename определяет путь, специфичный для ОС. Путь может быть абсолютным или относительным; относительные пути разрешаются относительно текущей рабочей папки.

Необязательный аргумент name задает имя теста; по умолчанию или, если None, os.path.basename(filename) содержит используемое значение.

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

Необязательный аргумент globs предоставляет словарь, который должен быть использован в качестве глобалов при выполнении примеров. Новая неглубокая копия этого словаря создается для doctest, поэтому ее примеры начинаются с чистого листа. По умолчанию или, если None, используется новый пустой словарь.

Необязательный аргумент extraglobs предоставляет словарь, сливаемый с глобальным используемый, для выполнения примеров. Он работает как dict.update(): если globs и extraglobs содержат общий ключ, ассоциированный значение в extraglobs появляется в комбинированном словаре. По умолчанию, или если None, никакие дополнительные globals не используются. Это расширенная функция, позволяющая параметризовать доктесты. Например, доктест может быть написан для базового класса, используя родное имя класса, затем снова использоваться чтобы проверить любое количество подклассов, передав extraglobs словарь, отображающий родное имя подкласса, которое будет проверено.

Дополнительный аргумент verbose печатает много подробностей, если True, и печатает только ошибки, если false; по умолчанию или, если None, это верно тогда и только тогда, когда '-v' присутствует в sys.argv.

Необязательный аргумент report печатает сводку в конце, когда true, иначе ничего не печатает в конце. В подробном режиме сводка является подробной, иначе сводка очень краткая (фактически пуста, если все тесты прошли).

Необязательный аргумент optionflags (по умолчанию значение 0) принимает побитовое или флагов параметров. См. раздел Флаги выбора.

Необязательный аргумент raise_on_error по умолчанию содержит значение false. Если значение равно true, при первом сбое или при непредвиденном исключении в примере возникает исключение. Это позволяет выполнять отладку отказов после ошибок. Поведение по умолчанию - продолжение выполнения примеров.

Необязательный аргумент parser указывает DocTestParser (или подкласс), который должен быть применен для извлечения тестов из файлов. По умолчанию используется обычный парсер (т.е. DocTestParser()).

Необязательный аргумент encoding указывает кодировку, которая должена быть используема для преобразования файла в Юникод.

doctest.testmod(m=None, name=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, exclude_empty=False)

Все аргументы необязательны, и все, кроме m, должны быть указаны в ключевой форме.

Примеры тестов в докстрингах в функциях и классах, доступных из модуля m (или модуля __main__, если m не поставляется или является None), начиная с m.__doc__.

Также тестовые примеры, доступные из словаря m.__test__, если он существует и не является None. m.__test__ отображает названия (строки) на функции, классы и строки; функция и класс докстрингов - найденный для примеров; строки - найденный непосредственно, как будто они были докстрингами.

Только докстринги, приложенные к объектам, принадлежащие модулю m, являются найденный.

Возвращает (failure_count, test_count).

Необязательный аргумент name задает имя модуля; по умолчанию или, если None, используется m.__name__.

Необязательный аргумент exclude_empty по умолчанию содержит значение false. При значении true объекты, для которых не найдены доктесты, исключаются из рассмотрения. По умолчанию хак для обратной совместимости, так, чтобы код, все еще используя doctest.master.summarize() вместе с testmod() продолжил получение вывода для объектов без тестов. Аргумент exclude_empty для более нового конструктора DocTestFinder по умолчанию содержит значение true.

Дополнительные аргументы extraglobs, verbose, report, optionflags, raise_on_error и globs совпадают с функцией testfile() выше, за исключением того, что по умолчанию globs равен m.__dict__.

doctest.run_docstring_examples(f, globs, verbose=False, name="NoName", compileflags=None, optionflags=0)

Примеры тестов, связанные с объектом f; например, f может быть строкой, модулем, функцией или объектом класса.

Неглубокая копия аргумента словаря globs используется для выполнения контекста.

Дополнительный аргумент name используется в сообщениях об ошибках и содержит значение по умолчанию "NoName".

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

Дополнительный аргумент compileflags задает набор флагов, которые должны быть используемы компилятором Python при запуске примеров. По умолчанию или, если None, флаги выводятся в соответствии с набором будущих элементов, найденных в globs.

Дополнительный аргумент optionflags работает как для функции testfile() выше.

Unittest API

По мере роста вашей коллекции доктестируемых модулей вам будет нужен способ систематизации выполнения всех доктестов. doctest обеспечивает две функции, которые могут быть используемый, чтобы создать наборы тестов unittest из модулей и текстовых файлов, содержащих доктестов. Для интеграции с обнаружением теста unittest включите функцию load_tests() в модуль тестирования:

import unittest
import doctest
import my_module_with_doctests

def load_tests(loader, tests, ignore):
    tests.addTests(doctest.DocTestSuite(my_module_with_doctests))
    return tests

Существует две основные функции для создания unittest.TestSuite сущности из текстовых файлов и модулей с доктестами:

doctest.DocFileSuite(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None)

Преобразование тестов doctest из одного или нескольких текстовых файлов в unittest.TestSuite.

Возвращаемый unittest.TestSuite запускает фреймворк unittest и управлять интерактивными примерами в каждом файле. Если пример в каком-либо файле завершается неуспешно, то тест синтезированного блока завершается неуспешно, и возникает исключение failureException, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.

Передайте один или несколько путей (как строки) текстовым файлам, которые необходимо проверить.

Параметры могут быть предоставлены в виде ключевых аргументов:

Необязательный аргумент module_relative указывает, как следует интерпретировать имена файлов в paths:

  • Если module_relative содержит значение True (по умолчанию), то каждое имя файла в paths указывает путь к модулю, не зависящий от ОС. По умолчанию этот путь относится к каталогу вызывающего модуля; но если указан аргумент package, то он относится к этому пакету. Для обеспечения независимости от ОС каждое имя файла должно использовать символы / для разделения сегментов пути и не может быть абсолютным путем (т.е. оно может начинаться не с /).
  • Если module_relative является False, то каждое имя файла в paths указывает путь, специфичный для ОС. Путь может быть абсолютным или относительным; относительные пути разрешаются относительно текущей рабочей папки.

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

Дополнительный аргумент setUp указывает функцию настройки для набора тестов. Это вызывается перед выполнением тестов в каждом файле. Функция setUp будет передана объекту DocTest. Функция установки может получить доступ к клобальному тесту, когда передан globs атрибут теста.

Дополнительный аргумент tearDown указывает функцию выделения для набора тестов. Это вызывается после выполнения тестов в каждом файле. Функция tearDown будет передана объекту DocTest. Функция установки может получить доступ к клобальному тесту, когда передан globs атрибут теста.

Необязательный аргумент globs - это словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию globs является новым пустым словарем.

Необязательный аргумент optionflags указывает параметры doctest по умолчанию для тестов, созданные отдельными флагами опций. См. раздел Флаги выбора. Для получения более эффективного способа настройки параметров отчетности см. функцию set_unittest_reportflags() ниже.

Необязательный аргумент parser указывает DocTestParser (или подклассу), который используется для извлечения тестов из файлов. По умолчанию используется обычный парсер (т.е. DocTestParser()).

Необязательный аргумент encoding указывает кодировку, которая должена быть используема для преобразования файла в Юникод.

Глобальная __file__ добавляется к глобалям, предоставляемым для тестирования, загружаемого из текстового файла с помощью DocFileSuite().

doctest.DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None)

Преобразование тестов доктестов для модуля в unittest.TestSuite.

возвращенный:class:unittest.TestSuite должен управлять unittest фреймворк и управляет каждым doctest в модуле. Если какой-либо из тестов не проходит, то тест синтезированного блока завершается неуспешно, и возникает исключение failureException, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.

Дополнительный аргумент module предоставляет модуль для тестирования. Это может быть объект модуля или (возможно, пунктирное) имя модуля. Если не указан, модуль, вызывающий эту функцию, является используемый.

Необязательный аргумент globs - это словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию globs является новым пустым словарем.

Дополнительный аргумент extraglobs указывает дополнительный набор глобальных переменных, который объединяется в globs. По умолчанию никакие дополнительные globals не используемый.

Дополнительный test_finder аргумента - объект DocTestFinder (или общедоступная замена), который является используемый, чтобы извлечь doctests из модуля.

Необязательные аргументы setUp, tearDown и optionflags такие же, как и для функции DocFileSuite() выше.

Эта функция использует тот же метод поиска, что и testmod().

Изменено в версии 3.5: DocTestSuite() возвращает пустой unittest.TestSuite, если module не содержит докстринги вместо поднятия исключения ValueError.

Под прикрытием DocTestSuite() создает unittest.TestSuite из doctest.DocTestCase сущности, а DocTestCase - подкласс unittest.TestCase. DocTestCase не документирован здесь (внутренняя деталь), но изучение его кода поможет ответить на вопросы о точных деталях интеграции unittest.

Аналогично, DocFileSuite() создает unittest.TestSuite из doctest.DocFileCase сущности, а DocFileCase - подкласс DocTestCase.

Итак существует оба способа создать unittest.TestSuite работающих сущности DocTestCase. Это важно по тонкой причине: когда вы запускаете функции doctest самостоятельно, вы можете управлять опциями doctest в использовании непосредственно, передавая флаги опций в функции doctest. Однако, если вы пишете для unittest фреймворка, unittest в конечном итоге контролирует, когда и как выполняются тесты. Автор фреймворка, как правило, хочет управлять опциями сообщений doctest (возможно, например, определенный параметрами командной строки), но нет никакого способа передать опции через unittest к запускаемым тестам doctest.

По этой причине doctest также поддерживает понятие doctest отчетов флагов, специфичных для поддержки unittest, с помощью этой функции:

doctest.set_unittest_reportflags(flags)

Установить используемые флаги сообщений doctest .

Аргумент flags принимает побитовое ИЛИ флагов параметров. См. раздел Флаги выбора. Только «отчетные флаги» могут быть используемы.

Это глобальный параметр модуля и влияет на все будущие доктесты, выполняемые модулем unittest: метод runTest() DocTestCase просматривает флаги опций, заданные для тестового случая при построении DocTestCase сущности. Если флаги отчетов не были определены (является типичным и ожидаемым случаем), флаги сообщения doctest unittest - побитово ИЛИ с опциональными флагами, и дополнительными опциональными флагами передаются DocTestRunner сущности, созданной, чтобы управлять doctest. Если какие-либо флаги отчетов были определены, когда экземпляр DocTestCase был создан, флаги отчетов doctest unittest игнорируются.

Значение флагов отчетов unittest влияют перед вызовом функции, возвращаемой функцией.

Продвинутый API

Базовый API представляет собой простую обертку, предназначенную для упрощения использования doctest. Она является достаточно гибкой и должна удовлетворять потребности большинства пользователей; однако, если требуется более тонкий контроль за тестированием или требуется расширить возможности doctest, следует использовать продвинутое API.

Продвинутое API вращается вокруг двух контейнерных классов, которые применяются для хранения интерактивных примеров, извлеченных из doctest примеров:

  • Example: одна Python инструкция, в паре с ожидаемым выводом.
  • DocTest: коллекция Exampleов, как правило, извлеченная из единственного докстринга или текстового файла.

Дополнительные классы обработки определяются для поиска, анализа, выполнения и проверки примеров doctest:

  • DocTestFinder: находит все докстринги в данном модуле и использует DocTestParser для создания DocTest из каждого докстринга, содержащего интерактивные примеры.
  • DocTestParser: создает объект DocTest из строки (такого как докстринг объект).
  • DocTestRunner: выполняет примеры в DocTest и использует OutputChecker для проверки их выходных данных.
  • OutputChecker: сравнение фактических выходных данных из примера doctest с ожидаемыми выходными данными и определения их соответствия.

Отношения между этими классами обработки суммированы на следующей диаграмме:

                            list of:
+------+                   +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+    |        ^     +---------+     |       ^    (printed)
            |        |     | Example |     |       |
            v        |     |   ...   |     v       |
           DocTestParser   | Example |   OutputChecker
                           +---------+

Объекты DocTest

class doctest.DocTest(examples, globs, name, filename, lineno, docstring)

Коллекция примеров doctest, которые должны выполняться в одном пространстве имен. Аргументы конструктора используются, чтобы инициализировать атрибуты тех же имен.

DocTest определяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.

examples

Список Example объектов кодирующих отдельные интерактивные примеры Python, которыми должены быть выполнены этим тестом.

globs

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

name

Строковое имя, идентифицирующее DocTest. Обычно это имя объекта или файла, из которого был извлечен тест.

filename

Название файла, из которого был извлечен этот DocTest; или None, если имя файла неизвестно, или если DocTest не был извлечен из файла.

lineno

Номер строки в filename, где DocTest начинается, или None, если номер строки недоступен. Этот номер строки отсчитывается от нуля относительно начала файла.

docstring

Строка, из которой был извлечен тест, или None, если строка недоступена, или если тест не был извлечен из строки.

Объекты Example

class doctest.Example(source, want, exc_msg=None, lineno=0, indent=0, options=None)

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

Example определяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.

source

Строка, содержащая исходный код примера. Этот исходный код состоящий из одной Python инструкции, и всегда заканчивается новой строкой; при необходимости конструктор добавляет новую строку.

want

Ожидаемый вывод от запуска исходного кода примера (либо от stdout, либо от трейсбэка в случае исключения). want заканчивается символом новой строки, если никакой вывод не ожидается, в этом случае это - пустая строка. При необходимости конструктор добавляет новую строку.

exc_msg

Сообщение об исключении, созданное в примере, если ожидается, что пример создаст исключение; или None, если не предполагается создание исключения. Это сообщение об исключении сравнивается с возвращаемым значением traceback.format_exception_only(). exc_msg заканчивается символом новой строки, если она не None. При необходимости конструктор добавляет символ новой строки.

lineno

Номер строки в строке, содержащей этот пример, где пример начинается. Номер строки начинается с нуля относительно начала содержащего строку.

indent

Отступы примера в содержащейся строке, т.е. число символов пробела, предшествующее первому приглашению (>>>) примера.

options

Словарь, отображающий опциональные флаги в True или False, который используется для переопределения параметров по умолчанию для этого примера. Флаги любых опций, не содержавшиеся в этом словаре, остаются в их значениях по умолчанию (как определено DocTestRunner optionflags). По умолчанию параметры не установлены.

Объекты DocTestFinder

class doctest.DocTestFinder(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)

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

Опциональным аргументом verbose можно показать объекты найденные поисковиком. По умолчанию используется значение False (без вывода).

Опциональный аргумент parser определяет объект DocTestParser (или выпадающая замена), который используется для извлечения доктестов из докстрингов.

Если необязательный аргумент recurse содержит значение false, то DocTestFinder.find() будет проверять только данный объект, но не содержащиеся в нем объекты.

Если необязательный аргумент exclude_empty содержит значение false, то DocTestFinder.find() будет включать тесты для объектов с пустым докстрингами.

DocTestFinder определяет следующий метод:

find(obj[, name][, module][, globs][, extraglobs])

Возвращает список DocTestов, которые определены в докстрингах obj или любой другого объекта содержащего докстринги .

Необязательный аргумент name указывает имя объекта; это имя будет используемо для построения имен возвращаемых DocTestов. Если name не указан, то используется obj.__name__.

Необязательный параметр module - это модуль, содержащий данный объект. Если модуль не будет определен или будет None, то тестовый поисковик попытается автоматически определить правильный модуль. Модуль объектов используется:

  • Как пространство имен по умолчанию, если globs не определен.
  • Чтобы предотвратить извлечение DocCreateFinder из DocTests объектов, импортированных из других модулей. (Содержащиеся объекты с модулями, отличными от module, игнорируются.)
  • Поиск имени файла, содержащего объект.
  • Для поиска номера строки объекта в его файле.

Если module является False, то попытка найти модуль не будет предпринята. Это непонятно, из использования в основном в тестировании самого doctest: если module является False, или является None, но не может быть найден автоматически, то все объекты считаются принадлежащими (несуществующему) модулю, поэтому все содержащиеся объекты будут (рекурсивно) найденный для доктестов.

Глобалы для каждого DocTest образуются объединением globs и extraglobs (привязки в extraglobs переопределяют привязки в globs). Для каждого DocTest создается новая неглубокая копия словаря глобалов. Если globs не определен, то по умолчанию используется модуль __dict__, если определен, или {} иначе. Если параметр extraglobs не указан, по умолчанию устанавливается значение {}.

Объекты DocTestParser

class doctest.DocTestParser

Класс обработки, используемый для извлечения интерактивных примеров из строки и применяются для создания объекта DocTest.

DocTestParser определяет следующие методы:

get_doctest(string, globs, name, filename, lineno)

Извлекает все doctest примеры из данной строки и соберает их в объект DocTest.

globs, name, filename и lineno являются атрибутами для нового объекта DocTest. См. документацию для DocTest для получения дополнительной информации.

get_examples(string, name='<string>')

Извлекает все doctest примеры из данной строки и возвращает её как список объектов Example. Номера строк начинаются с 0. Необязательный аргумент name является именем, идентифицирующим эту строку и используется только для сообщений об ошибках.

parse(string, name='<string>')

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

Объекты DocTestRunner

class doctest.DocTestRunner(checker=None, verbose=None, optionflags=0)

Класс обработки используемый для выполнения и проверки интерактивных примеров в DocTest.

Сравнение ожидаемых и фактических результатов осуществляется с помощью OutputChecker. Это сравнение может быть настроено с помощью ряда флагов опций; для получения дополнительной информации см. раздел Флаги выбора. Если флагов опций недостаточно, сравнение также может быть настроено путем передачи конструктору подкласса OutputChecker.

Отображением вывода тестового раннера можно управлять двумя способами. Во-первых, выходная функция может быть передана в TestRunner.run(); эта функция будет вызвана с строкой, которую нужно показать. По умолчанию используется значение sys.stdout.write. Если захват выходных данных недостаточен, то вывод на экран также можно настроить с помощью подкласса DocCreateRunner и переопределения методов report_start(), report_success(), report_unexpected_exception() и report_failure().

Дополнительный checker ключевой аргумент определяет объект OutputChecker (или вставная замена), который применяется для сравнения ожидаемого вывода с фактическим выводом doctest примеров.

Дополнительный ключевой аргумент verbose управляет детализацией DocTestRunner. Если verbose - True, то информация печатается о каждом примере, так как он выполняется. Если verbose является False, то печатаются только отказы. Если verbose неуказанн, или None, то производится подробный вывод тогда и только тогда, когда установлен переключатель командной строки -v.

Дополнительный optionflags ключевой аргумент может быть используем для управления, как тестраннер сравнивает ожидаемый вывод с фактическим выводом, и как он покажет сбои. Дополнительные сведения см. в разделе Флаги выбора.

DocTestParser определяет следующие методы:

report_start(out, test, example)

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

example - пример, который будет обработан. test - тест содержащий пример. out - выходная функция, переданная DocTestRunner.run().

report_success(out, test, example, got)

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

example - пример, который будет обработан. got является фактическим выводом из примера. test - тест, содержащий пример. out - выходная функция, переданная DocTestRunner.run().

report_failure(out, test, example, got)

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

example - пример, который будет обработан. got является фактическим выводом из примера. test - тест, содержащий пример. out - выходная функция, переданная DocTestRunner.run().

report_unexpected_exception(out, test, example, exc_info)

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

example - пример, который будет обработан. exc_info - кортеж, содержащий информацию о неожиданном исключении (как возвращенный sys.exc_info()). test - тест, содержащий пример. out - выходная функция, переданная DocTestRunner.run().

run(test, compileflags=None, out=None, clear_globs=True)

Запустить примеры в test (объект DocTest) и отобразить результаты с помощью функции записи out.

Примеры выполняются в пространстве имен test.globs. Если clear_globs будет True (по умолчанию), то пространство имен будет очищено после запуска теста, чтобы помочь со сборкой мусора. Если вы хотите проверить пространство имен после завершения теста, используйте команду clear_globs=False.

compileflags задает набор флагов, которые должны быть используемы компилятором Python при запуске примеров. Если не указано, то по умолчанию используется набор флагов будущего импорта, которые применяются к globs.

Выходные данные каждого примера проверяются с помощью выходного средства проверки DocTestRunner, и результаты форматируются методами DocTestRunner.report_*().

summarize(verbose=None)

Распечатать сводку всех тестовых примеров, которые были выполнены в этом DocCreateRunner, и возвратить именованный кортеж TestResults(failed, attempted).

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

Объекты OutputChecker

class doctest.OutputChecker

Класс, применяется для проверки соответствия фактических выходных данных из примера doctest с ожидаемыми выходными данными. OutputChecker определяет два метода: check_output(), который сравнивает заданную пару выводов, и возвращает True, если они совпадаются; и output_difference(), который возвращает строку, описывающую различия между двумя выводами.

OutputChecker определяет следующие методы:

check_output(want, got, optionflags)

Возвращает True тогда и только тогда, когда фактический вывод примера (got) соответствует ожидаемой выводу (want). Строки всегда считаются совпадающими, если они идентичны; но в зависимости от того, какие флажки используются тестраннером, возможно также несколько неточных типов совпадений. Дополнительные сведения о флагах параметров см. в разделе Флаги выбора.

output_difference(example, got, optionflags)

Возвращает строку описывающую различия между ожидаемым выводом для данного примера (example) и фактическим выводом (got). optionflags - множество опций флагов, используемых для сравнения want и got.

Отладка

Doctest предоставляет несколько механизмов для отладки примеров doctest:

  • Несколько функций преобразуют доктесты в исполняемые программы Python, которые можно запустить в отладчике Python pdb.

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

  • Случаи unittest, произведенные DocTestSuite(), поддерживают метод debug(), определенный unittest.TestCase.

  • В примере doctest можно добавить вызов pdb.set_trace(), и при выполнении этой строки вы перейдете в отладчик Python. Затем можно проверить текущее значения переменных и т.д. Например, предположим, что a.py содержит только докстринг модуля:

    """
    >>> def f(x):
    ...     g(x*2)
    >>> def g(x):
    ...     print(x+3)
    ...     import pdb; pdb.set_trace()
    >>> f(3)
    9
    """
    

    Тогда интерактивная сессия Python может быть похожей:

    >>> import a, doctest
    >>> doctest.testmod(a)
    --Return--
    > <doctest a[1]>(3)g()->None
    -> import pdb; pdb.set_trace()
    (Pdb) list
      1     def g(x):
      2         print(x+3)
      3  ->     import pdb; pdb.set_trace()
    [EOF]
    (Pdb) p x
    6
    (Pdb) step
    --Return--
    > <doctest a[0]>(2)f()->None
    -> g(x*2)
    (Pdb) list
      1     def f(x):
      2  ->     g(x*2)
    [EOF]
    (Pdb) p x
    3
    (Pdb) step
    --Return--
    > <doctest a[2]>(1)?()->None
    -> f(3)
    (Pdb) cont
    (0, 3)
    >>>
    

Функции, которые преобразуют доктесты в Python код, и, возможно, запускают синтезированный код в отладчике:

doctest.script_from_examples(s)

Преобразование текста с примерами в сценарий.

Аргумент s является строкой, содержащей примеры doctest. Строка преобразуется в скрипт Python, где doctest примеры s преобразуются в обычные код, а все остальное преобразуется в комментарии Python. Созданный сценарий возвращается как строка. Например:

import doctest
print(doctest.script_from_examples(r"""
    Set x and y to 1 and 2.
    >>> x, y = 1, 2

    Print their sum:
    >>> print(x+y)
    3
"""))

отобразит:

  # Set x and y to 1 and 2.
  x, y = 1, 2
  #
  # Print their sum:
  print(x+y)
  # Expected:
  ## 3

Эта функция используется внутри других функций (см. ниже), но также может быть
полезна, когда вы хотите превратить интерактивный сеанс Python в Python
скрипт.
doctest.testsource(module, name)

Преобразование doctest для объекта в сценарий.

Аргумент module - это объект модуля или пунктирное имя модуля, содержащее объект, чьи доктесты представляют интерес. Аргумент name - это имя (в модуле) объекта с интересующими доктестами. Результатом является строка, содержащая докстринг объекта, преобразованная в сценарий Python, как описано для script_from_examples() выше. Например, если модуль a.py содержит функцию верхнего уровня f(), то:

import a, doctest
print(doctest.testsource(a, "a.f"))

печатает скриптовую версию функции f() докстринга, с доктестами преобразованными в код, а остальное помещается в комментарии.

doctest.debug(module, name, pm=False)

Отладка доктестов для объекта.

Аргументы module и name такие же, как и для функции testsource() выше. Синтезированный сценарий Python для докстринга именованного объекта записывается во временный файл, а затем этот файл запускается под управлением отладчика Python pdb.

Используется поверхностная копия module.__dict__ для локального и глобального контекста выполнения.

Дополнительный аргумент pm определяет, нужно ли использовать отладку после крушения сценария. Если pm содержит значение True, файл сценария запускается непосредственно и отладчик подключается только в том случае, если сценарий завершается путем вызова необработанного исключения. Если это так, то отладка после крушения вызывается через pdb.post_mortem(), передавая объект трейсбэка из необработанного исключения. Если pm не указан или содержит значение False, сценарий запускается в отладчике с самого начала посредством передачи соответствующего вызова exec() pdb.run().

doctest.debug_src(src, pm=False, globs=None)

Отладка доктестов в строка.

Аналогична функции debug() выше, за исключением того, что строка, содержащий примеры doctest, определяется непосредственно через аргумент src.

Необязательный аргумент pm содержит аналогичное значение, что и в функции debug() выше.

Дополнительный аргумент globs предоставляет словарь для использования в качестве локального и глобального контекста выполнения. Если не указан, или None, используется пустой словарь. Если указан, то используется поверхностная копия словаря.

Класс DebugRunner и специальные исключения, которые он может поднять, представляют нибольший интерес для авторов тестов фреймворка и будут только коротко изложены здесь. Посмотрите исходный код и особенно DebugRunner докстринг (который является doctest!) для более подробной информацией:

class doctest.DebugRunner(checker=None, verbose=None, optionflags=0)

Подкласс DocTestRunner, который поднимает исключение, как только обнаруживается ошибка. При возникновении непредвиденного исключения, возникает исключение UnexpectedException, содержащее тест, пример и исходное исключение. Если выходные данные не совпадают, возникает исключение DocTestFailure, содержащее тест, пример и фактический вывод.

Для получения информации о параметрах и методах конструктора см. документацию по DocTestRunner в разделе Продвинутый API.

Сущность DebugRunner может поднять два исключения:

exception doctest.DocTestFailure(test, example, got)

Исключение, поднимаемое DocTestRunner для сигнализации о том, что фактические выходные данные doctest примера не соответствуют ожидаемым выходным данным. Аргументы конструктора используются для инициализации атрибутов с теми же именами.

DocTestFailure определяет следующие атрибуты:

DocTestFailure.test

Объект DocTest, выполняемый при сбое примера.

DocTestFailure.example

Example, который потерпел неудачу.

DocTestFailure.got

Фактический вывод примера.

exception doctest.UnexpectedException(test, example, exc_info)

Исключение, вызванное DocTestRunner, чтобы показать, что пример doctest вызвал непредвиденное исключение. Аргументы конструктора используются для инициализации атрибутов с теми же именами.

UnexpectedException определяет следующие атрибуты:

UnexpectedException.test

Объект DocTest, выполняемый при сбое примера.

UnexpectedException.example

Example, который потерпел неудачу.

UnexpectedException.exc_info

Кортеж, содержащий сведения о непредвиденном исключении, возвращаемого sys.exc_info().

Мыльница

Как отмечалось во введении, у модуля doctest три основных применения:

  1. Проверка примеров в докстрингах.
  2. Регрессионное тестирование.
  3. Исполняемая документация/грамотное тестирование.

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

При написании докстрингов, тщательно выберите примеры с докстрингами. Это искусство, которое должно быть поначалу изучено и он может быть неестественно на первых порах. Примеры должны добавлять подлинное значение в документацию. Хороший пример часто многословен. Если делать это с осторожностью, примеры будут бесценны для ваших пользователей, и многократно окупит время, необходимое для их сбора и изменения в течение многих лет. Я до сих пор поражаюсь тем, как часто один из моих doctest примеров перестает работать после «безобидной» перемены.

Doctest также является отличным инструментом для регрессионного тестирования, особенно если вы не скупитесь на пояснительный текст. Перемежая прозу и примеры, становится намного проще отслеживать то, что на самом деле тестируется, и почему. Когда тест проваливается, хорошая проза может значительно облегчить выяснение того, в чем проблема и как она должна быть исправлена. Это правда, что вы могли бы написать обширные комментарии в тестировании на основе кода, но мало кто из программистов это делает. Многие обнаружили, что использование doctest подходов вместо этого приводит к гораздо более четким тестам. Возможно, это просто потому, что doctest писать немного проще, чем код, в то время как писать комментарии в код немного сложнее. Я думаю, что дело обстоит глубже: естественное отношение при написании теста на основе doctest заключается в том, что вы хотите объяснить тонкие моменты вашего программного обеспечения и проиллюстрировать их примерами. Это, в свою очередь, естественно приводит к тестовым файлам, которые начинаются с простейших функций и логически переходят к сложностям и краевым случаям. Связанное повествование - это результат, вместо набора изолированных функций, которые тестируют изолированные биты функциональности, кажущиеся случайными. Это другое отношение и дает разные результаты, размывая различие между тестированием и объяснением.

Регрессионное тестирование лучше всего ограничить выделенными объектами или файлами. Существует несколько вариантов организации тестов:

  • Запись текстовых файлов, содержащих тестовые примеры, в качестве интерактивных примеров и тестирование файлов с помощью testfile() или DocFileSuite(). Это рекомендуется, хотя проще всего сделать для новых проектов, разработанных с самого начала для использования doctest.
  • Определите функции с именами _regrtest_topic, состоящие из отдельных докстрингов, содержащих контрольные примеры для названных тем. Эти функции могут быть включены в тот же файл, что и модуль, или разделены на отдельный тестовый файл.
  • Определите отображение словаря __test__ из тем регрессионного теста в докстринги, содержащие контрольные примеры.

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

if __name__ == '__main__':
    import doctest
    flags = doctest.REPORT_NDIFF|doctest.FAIL_FAST
    if len(sys.argv) > 1:
        name = sys.argv[1]
        if name in globals():
            obj = globals()[name]
        else:
            obj = __test__[name]
        doctest.run_docstring_examples(obj, globals(), name=name,
                                       optionflags=flags)
    else:
        fail, total = doctest.testmod(optionflags=flags)
        print("{} failures out of {} tests".format(fail, total))

Сноски

[1]Примеры, содержащие как ожидаемый вывод, так и исключение, не поддерживаются. Попытка угадать, где один заканчивается, а другой начинается, слишком подвержена ошибкам, и это также запутывает тест.