Spec-Zone.ru › Python 3.7

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

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

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

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

Вот пример модуля, который является полным, но компактным:

"""
This is the "example" module.

The example module supplies one function, factorial().  For example,

>>> factorial(5)
120
"""

def factorial(n):
    """Return the factorial of n, an exact integer >= 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:  # catch a value like 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! Приступайте. В следующих разделах приведены полные подробности. Обратите внимание, что в стандартном наборе тестов и библиотеках Python есть много примеров doctest. Особенно полезные примеры можно найти в стандартном тестовом файле Lib/test/test_doctest.py.

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

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

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

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

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

python M.py

Это не отобразит ничего, если пример не пройдёт, в этом случае не пройденные пример(ы) и причина(ы) отказа(ов) выводятся в стандартный поток вывода, и последняя строка вывода — ***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 содержит следующее:

The ``example`` module
======================

Using ``factorial``
-------------------

This is an example text file in reStructuredText format.  First import
``factorial`` from the ``example`` module:

    >>> from example import factorial

Now use it:

    >>> 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() не отобразит ничего, если пример не пройдёт. Если пример не пройдёт, то не пройденные пример(ы) и причина(ы) отказа(ов) будут напечатаны в стандартный поток вывода в том же формате, что и в 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 на этих примерах см. в следующих разделах.

Какие строки документации рассматриваются?

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

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

<name of M>.__test__.K

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

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

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

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

>>> # comments are ignored
>>> 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) — нет (отслеживание исключений происходит другим способом).
  • Если вы продолжите строку с помощью обратного слеша в интерактивной сессии или по любой другой причине используете обратный слеш, вы должны использовать необработанную строку документации, которая сохранит ваши обратные слэши точно так, как вы их набираете:

    >>> def f(x):
    ...     r'''Backslashes in a raw docstring: m\n'''
    >>> print(f.__doc__)
    Backslashes in a raw docstring: m\n
    

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

    >>> def f(x):
    ...     '''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

Этот doctest выполняется успешно, если возбуждено исключение 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 или пример просто выводит этот текст трассировки. На практике обычный вывод редко начинается со строки заголовка трассировки стека, поэтому это не создаёт реальных проблем.
  • Каждая строка стека трассировки (если присутствует) должна быть отступом дальше, чем первая строка примера, или начинаться с небуквенно-цифрового символа. Первая строка, следующая за заголовком трассировки стека и имеющая одинаковый отступ и начинающаяся с буквенно-цифрового символа, считается началом детали исключения. Конечно, это работает корректно для реальных трассировок стека.
  • Когда опция doctest IGNORE_EXCEPTION_DETAIL указана, всё после левого двоеточия и любой информации о модуле в имени исключения игнорируется.
  • Интерактивная оболочка пропускает строку заголовка трассировки стека для некоторых исключений SyntaxError. Но doctest использует строку заголовка трассировки стека для различения исключений от неисключений. Поэтому в редком случае, когда вам нужно проверить исключение SyntaxError, которое опускает строку заголовка трассировки стека, вам нужно будет вручную добавить эту строку в пример теста.
  • Для некоторых исключений SyntaxError Python отображает позицию символа синтаксической ошибки, используя маркер ^:

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

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

    >>> 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, ожидающий вывод «маленького целого числа», всё ещё работает в этих случаях. Эта опция, вероятно, исчезнет, но не в ближайшие несколько лет.

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. Поэтому оба эти варианта будут работать со флагом, независимо от того, выполняется ли тест под Python 2.7 или Python 3.2 (или более поздними версиями):

>>> 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 также является единственным ясным способом написать doctest, который не обращает внимания на детали исключения, но продолжает проходить под 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, чтобы сказать «не» вместо «не».

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

doctest.SKIP

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

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

doctest.COMPARISON_FLAGS

Маска битов, объединяющая все вышеперечисленные флаги сравнения.

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

doctest.REPORT_UDIFF

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

doctest.REPORT_CDIFF

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

doctest.REPORT_NDIFF

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

doctest.REPORT_ONLY_FIRST_FAILURE

При указании отображается первый не пройденный пример в каждом doctest, но подавляется вывод для всех остальных примеров. Это предотвратит doctest от сообщения об успешных примерах, которые нарушаются из-за предыдущих сбоев; но это также может скрыть неправильные примеры, которые терпят неудачу независимо от первого сбоя. Когда 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, следующие за исходным кодом примера:

directive             ::=  "#" "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]

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

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

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

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

уязвим! Одно из решений — сделать

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

Другое — сделать

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

Примечание

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

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

Ещё одна плохая идея — печатать вещи, которые содержат адрес объекта, например

>>> id(1.0) # certain to fail some of the time
7948648
>>> class C: pass
>>> C()   # the default repr() for instances embeds an address
<__main__.C instance at 0x00AC18F0>

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

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

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

>>> 1./7  # risky
0.14285714285714285
>>> print(1./7) # safer
0.142857142857
>>> print(round(1./7, 6)) # much safer
0.142857

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

>>> 3./4  # utterly safe
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, дополнительные глобальные переменные не используются. Это расширенная функция, которая позволяет параметризировать doctests. Например, doctest может быть написан для базового класса, используя общее имя для класса, затем повторно использован для проверки любого количества подклассов путём передачи словаря extraglobs, сопоставляющего общее имя с подклассом, который следует проверить.

Необязательный аргумент verbose печатает много информации, если истинно, и печатает только ошибки, если ложно; по умолчанию, или если None, это истинно тогда и только тогда, когда '-v' находится в sys.argv.

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

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

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

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

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

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 по умолчанию ложно. Если истинно, объекты, для которых не найдены doctests, исключаются из рассмотрения. По умолчанию это фиксация обратной совместимости, чтобы код, использующий 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() выше.

API для модуля unittest

По мере роста вашей коллекции модулей с doctest'ами вам понадобится способ систематического запуска всех их doctests. doctest предоставляет две функции, которые могут быть использованы для создания наборов тестов unittest из модулей и текстовых файлов, содержащих doctests. Для интеграции с обнаружением тестов 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 из текстовых файлов и модулей с doctests:

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. Функция setUp может получить доступ к глобальным переменным теста как к атрибуту globs переданного теста.

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

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

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

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

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

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

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

Преобразовать тесты doctest для модуля в unittest.TestSuite.

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

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

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

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

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

Необязательные аргументы 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 принимает поразрядное ИЛИ флагов опций. См. раздел Флаги опций. Можно использовать только «флаги отчётности».

Это глобальная настройка модуля, которая влияет на все будущие тесты doctest, выполняемые модулем unittest: метод runTest() экземпляра DocTestCase проверяет флаги опций, заданные для тестового случая, когда экземпляр DocTestCase был создан. Если не были указаны флаги отчётности (что является типичным и ожидаемым случаем), флаги отчётности doctest’s unittest поразрядно объединяются с флагами опций, и так расширенные флаги опций передаются экземпляру DocTestRunner, созданному для выполнения doctest. Если при создании экземпляра DocTestCase были указаны какие-либо флаги отчётности, флаги отчётности doctest’s 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, либо traceback в случае исключения). want заканчивается новой строкой, если ожидаемый вывод отсутствует, в противном случае это пустая строка. Конструктор добавляет новую строку при необходимости.

exc_msg

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

lineno

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

indent

Отступ примера в содержащей строке, то есть число символов пробела, предшествующих первой подсказке примера.

options

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

Объекты DocTestFinder

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

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

Необязательный аргумент 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 не указан.
  • Для предотвращения извлечения DocTest из объектов, импортированных из других модулей. (Содержимые объекты с модулями, отличными от 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. Если захват вывода недостаточен, то вывод можно также настроить, создав подкласс DocTestRunner и переопределив методы 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 — тест, содержащий example. out — функция вывода, переданная в DocTestRunner.run().

report_success(out, test, example, got)

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

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

report_failure(out, test, example, got)

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

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

report_unexpected_exception(out, test, example, exc_info)

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

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

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

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

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

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

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

summarize(verbose=None)

Выводит сводку всех тестов, запущенных этим DocTestRunner, и возвращает кортеж 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:

  • Несколько функций преобразуют тесты doctest в исполняемые программы Python, которые могут быть запущены в отладчике Python, pdb.
  • Класс DebugRunner является подклассом DocTestRunner и генерирует исключение для первого неработающего примера, содержащего информацию об этом примере. Эта информация может быть использована для проведения постобработки отладки примера.
  • Тесты unittest, сгенерированные с помощью DocTestSuite(), поддерживают метод debug(), определенный в unittest.TestCase.
  • Вы можете добавить вызов pdb.set_trace() в пример doctest, и вы попадете в отладчик 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)
    >>>
    

Функции, преобразующие тесты doctest в 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 — это объект модуля или имя модуля через точки, содержащий объект, doctests которого представляют интерес. Аргумент name — это имя (внутри модуля) объекта с интересующими doctests. Результат — строка, содержащая текст документации объекта, преобразованный в скрипт Python, как описано для script_from_examples() выше. Например, если модуль a.py содержит функцию верхнего уровня f(), то

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

выводит скриптовый вариант документации функции f() с преобразованными тестами doctest в код, а всё остальное — в комментарии.

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

Отладить тесты doctest для объекта.

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

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

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

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

Отладить тесты doctest в строке.

Это аналогично функции 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

Пример, который завершился ошибкой.

DocTestFailure.got

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

exception doctest.UnexpectedException(test, example, exc_info)

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

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

UnexpectedException.test

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

UnexpectedException.example

Пример, который завершился ошибкой.

UnexpectedException.exc_info

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

Soapbox

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

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

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

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

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

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

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

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

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

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

© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.7/library/doctest.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API