Spec-Zone.ru › Python 3.5

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.

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

Самый простой способ начать использовать 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.

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

Ещё одно простое применение 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.

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

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

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

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

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

<name of M>.__test__.K

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

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

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

В большинстве случаев копирование и вставка интерактивного сеанса консоли работает нормально, но 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
    

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

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

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

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

3.4. А что насчет исключений?

Без проблем, при условии, что трассировка стека исключений является единственным выводом, производимым примером: просто вставьте трассировку. [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

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

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

  • Doctest не может догадаться, получен ли ожидаемый вывод из трассировки стека исключений или из обычного вывода. Поэтому, например, пример, который ожидает ValueError: 42 is prime будет пройден, независимо от того, ValueError фактически возникает или пример просто выводит этот текст трассировки. На практике обычный вывод редко начинается со строки заголовка трассировки, поэтому это не создает реальных проблем.
  • Каждая строка стека трассировки (если присутствует) должна быть отступающей сильнее, чем первая строка примера, или начинаться с символа, не являющегося буквой или цифрой. Первая строка после заголовка трассировки, отступающая одинаково и начинающаяся с буквы или цифры, рассматривается как начало детализации исключения. Разумеется, это работает правильно для реальных трассировок.
  • Когда указана опция doctest IGNORE_EXCEPTION_DETAIL, всё, что следует слева от двоеточия и любой информации о модуле в имени исключения, игнорируется.
  • Интерактивная оболочка опускает строку заголовка трассировки для некоторых SyntaxErrors. Но 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
    

3.5. Флаги опций

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

New in version 3.4: Опция командной строки -o.

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

doctest.DONT_ACCEPT_TRUE_FOR_1

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

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')

3.6. Директивы

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

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

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

>>> foo()
{"Hermione": "hippogryph", "Harry": "broomstick"}

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

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

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

>>> d = sorted(foo().items())
>>> d
[('Harry', 'broomstick'), ('Hermione', 'hippogryph')]

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

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

>>> 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

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

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

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

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

Необязательный аргумент 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 по умолчанию ложно. Если истинно, объекты, для которых не найдены doctest'ы, исключаются из рассмотрения. Значение по умолчанию — это хак обратной совместимости, так что код, всё ещё использующий doctest.master.summarize() в сочетании с testmod(), по-прежнему получает вывод для объектов без тестов. Аргумент exclude_empty для нового конструктора DocTestFinder по умолчанию равен истинному.

Необязательные аргументы 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 равен истинному, вывод генерируется даже в случае отсутствия ошибок. По умолчанию вывод генерируется только в случае ошибки примера.

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

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

5. API модуля Unittest

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

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 определяет стандартные опции doctest для тестов, создаваемые путём объединения индивидуальных флагов опций. Смотрите раздел Флаги опций. Смотрите функцию set_unittest_reportflags() ниже для лучшего способа установки опций отчёта.

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

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

Глобальная переменная __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, действовавшее до вызова функции, возвращается функцией.

6. Расширенный 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
                           +---------+

6.1. Объекты 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, если строка недоступна или тест не был извлечён из строки.

6.2. Объекты Example

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

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

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

source

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

want

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

exc_msg

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

lineno

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

indent

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

options

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

6.3. Объекты DocTestFinder

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

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

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

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

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

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

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

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

Возвращает список DocTest, определённых docstring объекта obj или docstring любых содержащихся в нём объектов.

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

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

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

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

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

6.4. Объекты 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 — имя, идентифицирующее эту строку, используется только в сообщениях об ошибках.

6.5. Объекты 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 равно True (по умолчанию), то это пространство имён очищается после выполнения теста, чтобы помочь с утилизацией памяти. Если вы хотите проверить пространство имён после завершения теста, используйте clear_globs=False.

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

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

summarize(verbose=None)

Печатает сводку всех тестов, запущенных этим DocTestRunner, и возвращает кортеж TestResults(failed, attempted).

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

6.6. Объекты 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.

7. Отладка

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)
    >>>
    

Функции, которые преобразуют doctests в код 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(), с doctests, преобразованными в код, а остальное — в комментарии.

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

Отлаживать doctests для объекта.

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

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

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

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

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

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

8. Soapbox

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

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

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

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

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

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

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

Когда вы разместили свои тесты в модуле, сам модуль может быть тестовым исполнителем. Когда тест терпит неудачу, вы можете организовать свой тестовый исполнитель для повторного выполнения только неудачного 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.5/library/doctest.html

Spec-Zone.ru

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