Документация Python неофициальный перевод
Содержание страницы

doctest – Тестирование интерактивных примеров Pythondoctest – Test interactive Python examples

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


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

  • Чтобы проверить, что докстринги модуля актуальны, убедившись, что все интерактивные примеры по-прежнему работают как описано.

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

  • Чтобы написать учебную документацию для пакета, обильно иллюстрированную примерами ввода-вывода. В зависимости от того, на чём делается акцент – на примерах или на пояснительном тексте, это напоминает «грамотное тестирование» (literate testing) или «исполняемую документацию» (executable documentation).

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

"""
Это модуль "example".

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

>>> factorial(5)
120
"""

def factorial(n):
    """Возвращает факториал n – целое число >= 0.

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(30)
    265252859812191058636308480000000
    >>> factorial(-1)
    Traceback (самый последний вызов последним):
        ...
    ValueError: n должно быть >= 0

    Факториалы чисел с плавающей точкой допустимы, но число должно быть точным целым:
    >>> factorial(30.1)
    Traceback (самый последний вызов последним):
        ...
    ValueError: n должно быть целым числом
    >>> factorial(30.0)
    265252859812191058636308480000000

    Оно также не должно быть нелепо большим:
    >>> factorial(1e100)
    Traceback (самый последний вызов последним):
        ...
    OverflowError: n слишком велико
    """

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


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

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

$ python example.py
$

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

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

И так далее, в конечном итоге завершаясь:

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

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

Простое использование: проверка примеров в докстрингахSimple Usage: Checking Examples in Docstrings

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

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

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

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

python M.py

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

Запустите его с флагом -v вместо этого:

python M.py -v

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

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

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

python -m doctest -v example.py

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

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

Простое использование: проверка примеров в текстовом файлеSimple Usage: Checking Examples in a Text File

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

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

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

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

python -m doctest -v example.txt

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

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

Как это работаетHow It Works

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

Какие docstrings проверяются?Which Docstrings Are Examined?

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

Кроме того, бывают случаи, когда тесты должны быть частью модуля, но не частью текста справки, поэтому тесты не должны включаться в docstring. Doctest ищет переменную уровня модуля с именем __test__ и использует её для поиска других тестов. Если M.__test__ существует, это должен быть словарь, где каждая запись сопоставляет имя (строку) с объектом функции, объектом класса или строкой. Docstrings объектов функций и классов, найденные из M.__test__, проверяются, а строки обрабатываются так, как если бы они были docstrings. В выводе ключ K из M.__test__ отображается с именем M.__test__.K.

Например, поместите этот блок кода в начало example.py:

__test__ = {
    'numbers': """
>>> factorial(6)
720

>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
"""
}

Значение example.__test__["numbers"] будет обрабатываться как docstring, и все тесты внутри него будут запущены. Важно отметить, что значение может быть сопоставлено функции, объекту класса или модулю; если так, doctest рекурсивно ищет в них docstrings, которые затем сканируются на наличие тестов.

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

Как распознаются примеры docstring?How are Docstring Examples Recognized?

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

>>> # комментарии игнорируются
>>> 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 (трассировки исключений перехватываются другим способом).

  • Если вы продолжаете строку обратной косой чертой в интерактивной сессии или по любой другой причине используете обратную косую черту, следует использовать сырой docstring, который сохранит обратные косые черты точно в том виде, как вы их ввели:

    >>> def f(x):
    ...     r'''Обратная косая черта в сырой докстринге: m\n'''
    >>> print(f.__doc__)
    Backslashes in a raw docstring: m\n
    

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

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

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

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

Что такое контекст выполнения?What’s the Execution Context?

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

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

Как насчёт исключений?What About Exceptions?

Никаких проблем, при условии, что трассировка – единственный вывод, выдаваемый примером: просто вставьте трассировку. [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, всё, что находится после самого левого двоеточия и любой информации о модуле в имени исключения, игнорируется.

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

  • Для некоторых исключений Python отображает положение ошибки с помощью маркеров ^ и тильд:

    >>> 1 + None
      File "<stdin>", line 1
        1 + None
        ~~^~~~~~
    TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
    

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

    >>> 1 + None
      File "<stdin>", line 1
        1 + None
        ^~~~~~~~
    TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
    

Флаги опцийOption Flags

Ряд опционных флагов управляют различными аспектами поведения 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

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

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

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

>>> raise Exception('message')
Traceback (most recent call last):
builtins.Exception: message

>>> raise Exception('message')
Traceback (most recent call last):
__main__.Exception: message

Обратите внимание, что ELLIPSIS можно также использовать для игнорирования\nдеталей сообщения об исключении, но такой тест всё равно может не пройти\nв зависимости от того, присутствует ли имя модуля и совпадает ли оно в точности.

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

doctest.SKIP

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

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

doctest.COMPARISON_FLAGS

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

Вторая группа опций управляет тем, как сообщается о неудачных тестах:

doctest.REPORT_UDIFF

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

doctest.REPORT_CDIFF

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

doctest.REPORT_NDIFF

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

doctest.REPORT_ONLY_FIRST_FAILURE

Если указан, отображается первый упавший пример в каждом доктесте, но вывод\nдля всех остальных примеров подавляется. Это предотвращает сообщение доктестом\nо корректных примерах, которые «ломаются» из-за предыдущих ошибок; но оно также может\nскрыть некорректные примеры, которые падают независимо от первой ошибки. Когда\nREPORT_ONLY_FIRST_FAILURE указан, остальные примеры всё равно выполняются\nи учитываются в общем количестве сообщённых ошибок; только\nвывод подавляется.

doctest.FAIL_FAST

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

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

Новое в версии 3.4.

doctest.REPORTING_FLAGS

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

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

doctest.register_optionflag(name)

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

MY_FLAG = register_optionflag('MY_FLAG')

ДирективыDirectives

Директивы доктестов могут использоваться для изменения флагов опций для отдельного примера. Директивы доктестов – это\nспециальные комментарии 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" | ...

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

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

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

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

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

>>> print(list(range(20)))  # doctest: +ELLIPSIS
[0, 1, ..., 18, 19]

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

>>> print(list(range(20)))  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[0,    1, ...,   18,    19]

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

>>> print(list(range(20)))  # doctest: +ELLIPSIS
...                         # doctest: +NORMALIZE_WHITESPACE
[0,    1, ...,   18,    19]

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

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

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

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

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

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

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

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

вместо этого. Другой способ – сделать

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

Есть и другие, но идея понятна.

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

>>> id(1.0)  # обязательно иногда будет ошибаться  
7948648
>>> class C: pass
>>> C()  # стандартный repr() для экземпляров содержит адрес   
<C object at 0x00AC18F0>

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

>>> C()  # doctest: +ELLIPSIS
<C object at 0x...>

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

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

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

>>> 3./4  # абсолютно безопасно
0.75

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

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

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

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

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

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

Необязательный аргумент 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__, если он существует. m.__test__ сопоставляет имена (строки) функциям, классам и строкам; строки документации функций и классов проверяются на наличие примеров; строки проверяются напрямую, как если бы они были строками документации.

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

Вернуть (failure_count, test_count).

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

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

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

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

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

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

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

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

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

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

Unittest API

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

import unittest
import doctest
import my_module_with_doctests

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

exception doctest.failureException

Когда doctests, преобразованные в модульные тесты с помощью DocFileSuite() или DocTestSuite(), завершаются неудачей, возникает это исключение, отображающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.

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

Аналогично, DocFileSuite() создаёт unittest.TestSuite из экземпляров doctest.DocFileCase, а DocFileCase является подклассом DocTestCase.

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

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

doctest.set_unittest_reportflags(flags)

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

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

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

Функция возвращает значение флагов отчётности unittest, действовавших до её вызова.

Расширенный APIAdvanced API

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

Расширенный API строится вокруг двух классов-контейнеров, которые используются для хранения интерактивных примеров, извлечённых из тестовых сценариев doctest:

  • Example: Одна инструкция (statement) 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
                           +---------+

Объекты DocTestDocTest Objects

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

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

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

examples

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

globs

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

name

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

filename

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

lineno

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

docstring

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

Объекты ExampleExample Objects

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

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

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

source

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

want

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

exc_msg

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

lineno

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

indent

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

options

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

Объекты DocTestFinderDocTestFinder objects

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 не указан.

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

  • Для поиска имени файла, содержащего объект.

  • Для помощи в определении номера строки объекта в его файле.

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

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

Объекты DocTestParserDocTestParser objects

class doctest.DocTestParser

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

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

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

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

globs, name, filename и lineno – это атрибуты нового объекта DocTest. Дополнительную информацию см. в документации DocTest.

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

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

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

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

Объекты DocTestRunnerDocTestRunner objects

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

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

report_start(out, test, example)

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

example – это пример, который будет обработан. test – это тест, содержащий пример. 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.

Объекты OutputCheckerOutputChecker objects

class doctest.OutputChecker

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

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

check_output(want, got, optionflags)

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

output_difference(example, got, optionflags)

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

ОтладкаDebugging

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

выводит:

# Установите x и y в 1 и 2.
x, y = 1, 2
#
# Выведите их сумму:
print(x+y)
# Ожидаемый результат:
## 3

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

doctest.testsource(module, name)

Преобразует доктест объекта в скрипт.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Информацию о параметрах конструктора и методах см. в документации для DocTestRunner в разделе Advanced API.

Экземпляры DebugRunner могут вызывать два исключения:

exception doctest.DocTestFailure(test, example, got)

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

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

DocTestFailure.test

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

DocTestFailure.example

Неудавшийся Example.

DocTestFailure.got

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

exception doctest.UnexpectedException(test, example, exc_info)

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

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

UnexpectedException.test

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

UnexpectedException.example

Неудавшийся Example.

UnexpectedException.exc_info

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

ТрибунаSoapbox

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

  1. Проверка примеров в docstrings.

  2. Регрессионное тестирование.

  3. Исполняемая документация / литературное тестирование.

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

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

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

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

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

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

  • Определите словарь __test__, отображающий темы регрессионных тестов на docstrings, содержащие тестовые примеры.

Если вы поместили тесты в модуль, сам модуль может быть тестовым раннером. Когда тест падает, можно настроить раннер на повторный запуск только упавшего 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))

Сноски