doctest — Тестирование интерактивных примеров Python
Модуль 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.
If the result is small enough to fit in an int, return an int.
Else return a long.
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> [factorial(long(n)) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> factorial(30)
265252859812191058636308480000000L
>>> factorial(30L)
265252859812191058636308480000000L
>>> 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)
265252859812191058636308480000000L
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(long(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 и библиотеках есть много примеров doctests. Особенно полезные примеры можно найти в стандартном файле тестов 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 или нет не влияет).
Начиная с Python 2.6, существует также сокращенный вариант команды для запуска 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.
Начиная с Python 2.6, существует также сокращенный вариант команды для запуска 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
Любые найденные классы проверяются аналогичным образом рекурсивно, чтобы проверить строковые описания содержащихся в них методов и вложенных классов.
Изменено в версии 2.4: Концепция «приватного имени» устарела и больше не документируется.
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 в каждом месте, где ожидается пустая строка.В версии 2.4:
<BLANKLINE>был добавлен; в предыдущих версиях не было способа использовать ожидаемый вывод, содержащий пустые строки. -
Все символы табуляции расширяются до пробелов, используя отступы в 8 столбцов. Табуляции в выводе, сгенерированном тестируемым кодом, не изменяются. Поскольку любые жесткие табуляции в примере вывода расширены, это означает, что если вывод кода включает жесткие табуляции, единственный способ, которым doctest может пройти, это если параметр
NORMALIZE_WHITESPACEили директива включены. В качестве альтернативы, тест можно переписать, чтобы захватить вывод и сравнить его с ожидаемым значением в качестве части теста. Такое обращение с табуляциями в исходном коде было достигнуто методом проб и ошибок и оказалось наименее подверженным ошибкам способом их обработки. Можно использовать другой алгоритм для обработки табуляций, написав пользовательский классDocTestParser.Изменено в версии 2.4: Расширение табуляций до пробелов является новым; предыдущие версии пытались сохранить жесткие табуляции с путающими результатами.
- Вывод в стандартный вывод (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.0и столько же ведущих символов пробела удаляются из ожидаемого вывода, сколько было в исходной
'>>> 'строке, которая начала пример.
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) сравниваются с типом и деталью исключения, а остальные игнорируются.
Изменено в версии 2.4: Предыдущие версии не могли обрабатывать многострочные детали исключений.
Лучшая практика — опустить стек трейсбэка, если это не добавляет существенной документационной ценности примеру. Поэтому последний пример, вероятно, лучше записать так:
>>> raise ValueError('multi\n line\ndetail')
Traceback (most recent call last):
...
ValueError: multi
line
detail
Обратите внимание, что трейсбэки обрабатываются очень особенно. В частности, в переписанном примере использование ... независимо от параметра doctest ELLIPSIS. Эллипсис в этом примере может быть опущен, или он может быть также тремя (или тремя сотнями) запятыми или цифрами, или отформатированным фрагментом из юмористического произведения.
Некоторые детали, которые нужно прочитать один раз, но не нужно запоминать:
- Doctest не может догадаться, получен ли ожидаемый вывод от трейсбэка исключения или от обычного вывода. Таким образом, например, пример, который ожидает
ValueError: 42 is prime, пройдет, как еслиValueErrorдействительно возбуждено, или если пример просто выводит этот текст трейсбэка. На практике обычный вывод редко начинается с строки заголовка трейсбэка, поэтому это не создает реальных проблем. - Каждая строка стека трейсбэка (если он присутствует) должна быть отступами больше, чем первая строка примера, или начинаться с символа, не являющегося буквенно-цифровым. Первая строка, следующая за заголовком трейсбэка с одинаковым отступом и начинающаяся с буквенно-цифрового символа, принимается как начало детали исключения. Конечно, это делает правильную вещь для подлинных трейсбэков.
- Когда параметр doctest
IGNORE_EXCEPTION_DETAILуказан, все, что следует за левым двоеточием и любой информацией о модуле в имени исключения, игнорируется. - Интерактивная оболочка опускает строку заголовка трейсбэка для некоторых
SyntaxErrors. Но doctest использует строку заголовка трейсбэка, чтобы различать исключения от неисключений. Поэтому в редких случаях, когда вам нужно проверитьSyntaxError, который опускает строку заголовка трейсбэка, вам нужно будет вручную добавить строку заголовка трейсбэка в ваш пример теста. -
Для некоторых
SyntaxErrorPython отображает позицию символа синтаксической ошибки, используя маркер^:>>> 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 определяет, соответствует ли фактический вывод ожидаемому выводу примера:
-
doctest.DONT_ACCEPT_TRUE_FOR_1 -
По умолчанию, если блок ожидаемого вывода содержит только
1, а блок фактического вывода содержит только1или толькоTrue, это считается соответствием, и аналогично для0противFalse. КогдаDONT_ACCEPT_TRUE_FOR_1указан, ни одна замена не допускается. По умолчанию поведение соответствует тому, что Python изменил возвращаемый тип многих функций с целого на булевый; doctest, ожидающий «целого числа», по-прежнему работает в этих случаях. Этот параметр, вероятно, исчезнет, но не в течение нескольких лет.
-
doctest.DONT_ACCEPT_BLANKLINE -
По умолчанию, если блок ожидаемого вывода содержит строку, содержащую только строку
<BLANKLINE>, то эта строка будет соответствовать пустой строке в фактическом выводе. Поскольку чисто пустая строка определяет ожидаемый вывод, это единственный способ указать, что ожидается пустая строка. КогдаDONT_ACCEPT_BLANKLINEуказан, эта замена не допускается.
-
doctest.NORMALIZE_WHITESPACE -
Когда указан, все последовательности пробелов (пробелов и новых строк) обрабатываются как равные. Любая последовательность пробелов в ожидаемом выводе будет соответствовать любой последовательности пробелов в фактическом выводе. По умолчанию пробелы должны точно совпадать.
NORMALIZE_WHITESPACEособенно полезен, когда строка ожидаемого вывода очень длинная, и вы хотите разбить ее на несколько строк в вашем исходном коде.
-
doctest.ELLIPSIS -
Когда указан, маркер эллипсиса (
...) в ожидаемом выводе может соответствовать любому подстроке в фактическом выводе. Это включает подстроки, охватывающие границы строк, и пустые подстроки, поэтому лучше всего сохранять простоту использования. Сложные случаи могут привести к тому же виду «опечатки, он соответствовал слишком многим!» неожиданностей, к которым.*склонен в регулярных выражениях.
-
doctest.IGNORE_EXCEPTION_DETAIL -
При указании, пример, ожидающий исключение, проходит, если возникает исключение ожидаемого типа, даже если подробности исключения не совпадают. Например, пример, ожидающий
ValueError: 42, пройдет, если фактически возникло исключениеValueError: 3*14, но потерпит неудачу, например, если возникаетTypeError.Он также игнорирует имя модуля, используемое в отчётах doctest Python 3. Следовательно, оба этих варианта будут работать с указанным флагом, независимо от того, выполняется ли тест под Python 2.7 или Python 3.2 (или более поздних версий):
>>> raise CustomError('message') Traceback (most recent call last): CustomError: message >>> raise CustomError('message') Traceback (most recent call last): my_module.CustomError: messageОбратите внимание, что
ELLIPSISтакже может использоваться для игнорирования деталей сообщения об исключении, но такой тест всё ещё может потерпеть неудачу в зависимости от того, отображаются ли детали модуля как часть имени исключения. ИспользованиеIGNORE_EXCEPTION_DETAILи подробностей из Python 2.3 также является единственным ясным способом написания теста doctest, который не учитывает детали исключения, но продолжает проходить под Python 2.3 или более ранними версиями (эти версии не поддерживают директивы doctest и игнорируют их как нерелевантные комментарии). Например:>>> (1, 2)[3] = 'moo' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: object doesn't support item assignment
проходит под Python 2.3 и более поздними версиями Python с указанным флагом, хотя в Python 2.4 подробности изменились, чтобы сказать «не» вместо «не».
Изменено в версии 2.7:
IGNORE_EXCEPTION_DETAILтеперь также игнорирует любую информацию, относящуюся к модулю, содержащему тестируемое исключение
-
doctest.SKIP -
При указании пример вообще не запускается. Это может быть полезно в контекстах, где примеры doctest служат как документацией, так и тестовыми случаями, и пример должен быть включён для целей документации, но не должен проверяться. Например, вывод примера может быть случайным; или пример может зависеть от ресурсов, которые недоступны для драйвера теста.
Флаг SKIP также можно использовать для временного «комментирования» примеров.
Новое в версии 2.5.
-
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.REPORTING_FLAGS -
Побитовая маска, объединяющая все флаги отчёта выше.
Новое в версии 2.4: Постоянные DONT_ACCEPT_BLANKLINE, NORMALIZE_WHITESPACE, ELLIPSIS, IGNORE_EXCEPTION_DETAIL, REPORT_UDIFF, REPORT_CDIFF, REPORT_NDIFF, REPORT_ONLY_FIRST_FAILURE, COMPARISON_FLAGS и REPORTING_FLAGS были добавлены.
Существует также способ регистрации новых имён флагов опций, хотя это бесполезно, если вы не планируете расширять внутренние части doctest с помощью наследования:
-
doctest.register_optionflag(name) -
Создаёт новый флаг опции с заданным именем и возвращает целое значение нового флага.
register_optionflag()можно использовать при наследовании отOutputCheckerилиDocTestRunner, чтобы создать новые опции, которые поддерживаются вашими подклассами.register_optionflag()всегда следует вызывать с помощью следующего выражения:MY_FLAG = register_optionflag('MY_FLAG')Новое в версии 2.4.
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 range(20) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Без директивы он потерпит неудачу, как потому, что фактический вывод не имеет двух пробелов перед элементом списка с одной цифрой, так и потому, что фактический вывод находится в одной строке. Этот тест также проходит и также требует директивы для этого:
>>> print range(20) [0, 1, ..., 18, 19]
Несколько директив могут использоваться в одной физической строке, разделённые запятыми:
>>> print range(20) [0, 1, ..., 18, 19]
Если для одного примера используются несколько комментариев директивы, то они объединяются:
>>> print range(20) ... [0, 1, ..., 18, 19]
Как показывает предыдущий пример, вы можете добавлять ... строки в свой пример, содержащие только директивы. Это может быть полезно, когда пример слишком длинный, чтобы директива удобно уместилась в одной строке:
>>> print range(5) + range(10,20) + range(30,40) + range(50,60) ... [0, ..., 4, 10, ..., 19, 30, ..., 39, 50, ..., 59]
Обратите внимание, что поскольку все опции отключены по умолчанию, и директивы применяются только к примеру, в котором они появляются, включение опций (через + в директиве) обычно является единственным осмысленным выбором. Однако флаги опций также могут передаваться функциям, которые запускают doctests, устанавливая разные значения по умолчанию. В таких случаях выключение опции с помощью - в директиве может быть полезным.
Новое в версии 2.4: Поддержка директив doctest была добавлена.
3.7. Предупреждения
doctest серьёзно относится к требованию точных совпадений в ожидаемом выводе. Если не совпадает даже один символ, тест терпит неудачу. Это, вероятно, удивит вас несколько раз, когда вы узнаете именно то, что Python делает и не гарантирует относительно вывода. Например, при печати словаря Python не гарантирует, что пары ключ-значение будут выводиться в каком-либо определённом порядке, поэтому тест, подобный
>>> foo()
{"Hermione": "hippogryph", "Harry": "broomstick"}
уязвим! Одно решение — сделать
>>> foo() == {"Hermione": "hippogryph", "Harry": "broomstick"}
True
Другое — сделать
>>> d = foo().items()
>>> d.sort()
>>> 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][, name][, package][, globs][, verbose][, report][, optionflags][, extraglobs][, raise_on_error][, parser][, encoding]) -
Все аргументы, кроме 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 выводит много информации, если true, и выводит только ошибки, если false; по умолчанию или если
None, он равен true тогда и только тогда, когда'-v'находится вsys.argv.Необязательный аргумент report выводит сводку в конце, если true, иначе ничего не выводит. В режиме verbose сводка подробная, иначе сводка очень краткая (на самом деле пустая, если все тесты пройдены).
Необязательный аргумент optionflags объединяет флаги опций. См. раздел Флаги опций.
Необязательный аргумент raise_on_error по умолчанию равен false. Если true, исключение генерируется при первой ошибке или непредвиденном исключении в примере. Это позволяет отладить ошибки с помощью post-mortem отладки. По умолчанию выполнение примеров продолжается.
Необязательный аргумент parser задаёт
DocTestParser(или подкласс), который должен использоваться для извлечения тестов из файлов. По умолчанию используется стандартный парсер (т.е.,DocTestParser()).Необязательный аргумент encoding задаёт кодировку, которая должна быть использована для преобразования файла в unicode.
Введено в версии 2.4.
Изменено в версии 2.5: Добавлен параметр encoding.
- Если module_relative равно
-
doctest.testmod([m][, name][, globs][, verbose][, report][, optionflags][, extraglobs][, raise_on_error][, exclude_empty]) -
Все аргументы необязательны, и все, кроме m, должны быть указаны в виде ключевых аргументов.
Проверьте примеры в строках документации в функциях и классах, доступных из модуля m (или модуля
__main__, если m не указан или равноNone), начиная сm.__doc__.Также проведите тесты на примеры, доступные из словаря
m.__test__, если он существует и неNone.m.__test__сопоставляет имена (строки) с функциями, классами и строками; строки документации функций и классов проверяются на примеры; строки проверяются непосредственно, как если бы они были строками документации.Проверяются только строки документации, относящиеся к объектам, принадлежащим модулю m.
Вернуть
(failure_count, test_count).Необязательный аргумент name задаёт имя модуля; по умолчанию или если
None, используетсяm.__name__.Необязательный аргумент exclude_empty по умолчанию равен false. Если true, объекты, для которых не найдены doctest'ы, исключаются из рассмотрения. По умолчанию это откат назад для совместимости, чтобы код, использующий
doctest.master.summarize()в сочетании сtestmod(), по-прежнему получал вывод для объектов без тестов. Аргумент exclude_empty в новом конструктореDocTestFinderпо умолчанию равен true.Необязательные аргументы extraglobs, verbose, report, optionflags, raise_on_error и globs такие же, как для функции
testfile()выше, за исключением того, что globs по умолчаниюm.__dict__.Изменено в версии 2.3: Добавлен параметр optionflags.
Изменено в версии 2.4: Добавлены параметры extraglobs, raise_on_error и exclude_empty.
Изменено в версии 2.5: Необязательный аргумент isprivate, устаревший в 2.4, был удалён.
-
doctest.run_docstring_examples(f, globs[, verbose][, name][, compileflags][, optionflags]) -
Проверить примеры, связанные с объектом f; например, f может быть строкой, модулем, функцией или объектом класса.
Для контекста выполнения используется поверхностная копия словаря аргумента globs.
Необязательный аргумент name используется в сообщениях об ошибках и по умолчанию равен
"NoName".Если необязательный аргумент verbose равен true, вывод генерируется даже если ошибок нет. По умолчанию вывод генерируется только в случае ошибки примера.
Необязательный аргумент compileflags задаёт набор флагов, которые должен использовать Python-компилятор при выполнении примеров. По умолчанию или если
None, флаги выводятся соответствующим образом к набору новых возможностей, найденных в globs.Необязательный аргумент optionflags работает так же, как для функции
testfile()выше.
5. API модуля unittest
По мере роста коллекции модулей doctest'ов вам потребуется способ систематического запуска всех их doctest'ов. До Python 2.4, doctest имел едва документированный Tester класс, который обеспечивал примитивный способ объединения doctest'ов из нескольких модулей. Tester был слабым, и на практике большинство серьёзных фреймворков Python для тестирования построены на модуле unittest, который предоставляет много гибких способов объединения тестов из нескольких источников. Поэтому в Python 2.4 класс 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][, package][, setUp][, tearDown][, globs][, optionflags][, parser][, encoding]) -
Преобразование тестов 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.
Введено в версии 2.4.
Изменено в версии 2.5: Глобальная переменная
__file__была добавлена к глобальным переменным, предоставляемым doctest, загруженным из текстового файла с помощьюDocFileSuite().Изменено в версии 2.5: Аргумент encoding был добавлен.
Примечание
В отличие от
testmod()иDocTestFinder, эта функция вызывает исключениеValueError, если module не содержит строку документации. Вы можете предотвратить эту ошибку, передав экземплярDocTestFinderв качестве аргумента test_finder с ключевым аргументом exclude_empty установленным вFalse:>>> finder = doctest.DocTestFinder(exclude_empty=False) >>> suite = doctest.DocTestSuite(test_finder=finder)
- Если module_relative равен
-
doctest.DocTestSuite([module][, globs][, extraglobs][, test_finder][, setUp][, tearDown][, checker]) -
Преобразование тестов doctest для модуля в
unittest.TestSuite.Возвращаемая
unittest.TestSuiteдолжна быть запущена фреймворком unittest и выполняет каждый doctest в модуле. Если любой из doctest завершается ошибкой, то синтезированный юнит-тест завершается ошибкой, и возникает исключениеfailureExceptionс указанием имени файла, содержащего тест, и (иногда приблизительного) номера строки.Необязательный аргумент module предоставляет модуль для тестирования. Он может быть объектом модуля или именем модуля (возможно, с точками). Если не указано, используется модуль, вызывающий эту функцию.
Необязательный аргумент globs — словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию globs — новый пустой словарь.
Необязательный аргумент extraglobs определяет дополнительный набор глобальных переменных, который объединяется с globs. По умолчанию дополнительные глобальные переменные не используются.
Необязательный аргумент test_finder — объект
DocTestFinder(или его заменитель), который используется для извлечения doctest из модуля.Необязательные аргументы setUp, tearDown и optionflags такие же, как и для функции
DocFileSuite()выше.Введено в версии 2.3.
Изменено в версии 2.4: Добавлены параметры globs, extraglobs, test_finder, setUp, tearDown и optionflags; эта функция теперь использует ту же технику поиска, что и
testmod().
Внутри, 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дляunittestпобитово объединяются с флагами опций, а так изменённые флаги опций передаются экземпляруDocTestRunner, созданному для выполнения doctest. Если при создании экземпляраDocTestCaseбыли указаны какие-либо флаги отчёта, флаги отчётаdoctestдляunittestигнорируются.Значение флагов отчёта
unittest, действующее до вызова функции, возвращается функцией.Введено в версии 2.4.
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, которые должны быть выполнены в одном пространстве имён. Аргументы конструктора используются для инициализации атрибутов с теми же именами.
Введено в версии 2.4.
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][, lineno][, indent][, options]) -
Один интерактивный пример, состоящий из оператора Python и ожидаемого вывода. Аргументы конструктора используются для инициализации атрибутов с теми же именами.
Введено в версии 2.4.
Exampleопределяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться непосредственно.-
source -
Строка, содержащая исходный код примера. Этот исходный код состоит из одного оператора Python и всегда заканчивается новой строкой; конструктор добавляет новую строку при необходимости.
-
want -
Ожидаемый вывод от выполнения исходного кода примера (либо из stdout, либо отслеживания ошибки в случае исключения).
wantзаканчивается новой строкой, если ожидаемый вывод непустой; в противном случае это пустая строка. Конструктор добавляет новую строку при необходимости.
-
exc_msg -
Сообщение об исключении, сгенерированное примером, если пример ожидается для генерации исключения; или
Noneесли исключение не ожидается. Это сообщение об исключении сравнивается со значением, возвращаемымtraceback.format_exception_only().exc_msgзаканчивается новой строкой, если неNone. Конструктор добавляет новую строку при необходимости.
-
lineno -
Номер строки в строке, содержащей этот пример, где пример начинается. Этот номер строки является нулевым относительно начала содержащей строки.
-
indent -
Отступ примера в содержащей строке, т.е. количество пробелов, предшествующих первому приглашению примера.
-
options -
Словарь, сопоставляющий флаги опций с
TrueилиFalse, который используется для переопределения значений по умолчанию для данного примера. Любые флаги опций, не содержащиеся в этом словаре, оставляются со значением по умолчанию (как указано вDocTestRunner’soptionflags). По умолчанию не заданы опции.
-
6.3. Объекты DocTestFinder
-
class doctest.DocTestFinder([verbose][, parser][, recurse][, exclude_empty]) -
Класс обработки, используемый для извлечения
DocTestобъектов, относящихся к заданному объекту, из его docstring и docstring содержащихся в нём объектов.DocTestмогут в настоящее время быть извлечены из следующих типов объектов: модулей, функций, классов, методов, статических методов, методов класса и свойств.Необязательный аргумент verbose может использоваться для отображения объектов, просматриваемых поисковиком. По умолчанию он равен
False(без вывода).Необязательный аргумент parser задаёт объект
DocTestParser(или его аналог), используемый для извлечения тестов doctest из docstring.Если необязательный аргумент recurse имеет значение false, то
DocTestFinder.find()будет проверять только данный объект, а не любые содержащиеся в нём объекты.Если необязательный аргумент exclude_empty имеет значение false, то
DocTestFinder.find()будет включать тесты для объектов с пустыми docstring.Введено в версии 2.4.
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.Введено в версии 2.4.
DocTestParserопределяет следующие методы:-
get_doctest(string, globs, name, filename, lineno) -
Извлекает все примеры doctest из заданной строки и собирает их в объект
DocTest.globs, name, filename и lineno являются атрибутами нового объекта
DocTest. Дополнительную информацию см. в документации дляDocTest.
-
get_examples(string[, name]) -
Извлекает все примеры doctest из заданной строки и возвращает их в виде списка объектов
Example. Номера строк нумеруются с 0. Необязательный аргумент name — это имя, идентифицирующее эту строку, и используется только для сообщений об ошибках.
-
6.5. Объекты DocTestRunner
-
class doctest.DocTestRunner([checker][, verbose][, optionflags]) -
Класс обработки, используемый для выполнения и проверки интерактивных примеров в
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 может использоваться для управления тем, как тестовый запуск сравнивает ожидаемый вывод с фактическим выводом и как отображать ошибки. Дополнительную информацию см. в разделе Флаги параметров.
Добавлена в версии 2.4.
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][, out][, clear_globs]) -
Выполняет примеры в test (объект
DocTest) и отображает результаты с помощью функции записи out.Примеры выполняются в пространстве имен
test.globs. Если clear_globs равно True (по умолчанию), это пространство имен будет очищено после выполнения теста, чтобы помочь с сборкой мусора. Если вы хотите исследовать пространство имен после завершения теста, используйте clear_globs=False.compileflags задает набор флагов, которые должны использоваться компилятором Python при выполнении примеров. Если не указано, он по умолчанию будет содержать набор флагов импорта будущего, применимых к globs.
Вывод каждого примера проверяется с помощью контроллера вывода
DocTestRunner, а результаты форматируются с помощью методовDocTestRunner.report_*().
-
summarize([verbose]) -
Выводит сводку по всем тестам, которые были запущены данным DocTestRunner, и возвращает кортеж именованный кортеж
TestResults(failed, attempted).Необязательный аргумент verbose управляет подробностями сводки. Если подробность не указана, то используется подробность
DocTestRunner.Изменено в версии 2.6: Использование именованного кортежа.
-
6.6. Объекты OutputChecker
-
class doctest.OutputChecker -
Класс, используемый для проверки соответствия фактического вывода примера doctest ожидаемому выводу.
OutputCheckerопределяет два метода:check_output(), который сравнивает заданную пару выводов и возвращает True, если они совпадают; иoutput_difference(), который возвращает строку, описывающую различия между двумя выводами.Добавлена в версии 2.4.
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) print 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) print x 3 (Pdb) step --Return-- > <doctest a[2]>(1)?()->None -> f(3) (Pdb) cont (0, 3) >>>
Изменено в версии 2.4: Добавлена возможность полезного использования
pdb.set_trace()внутри doctest.
Функции, которые преобразуют doctest в код Python, а возможно и запускают сгенерированный код в отладчике:
-
doctest.script_from_examples(s) -
Преобразовать текст с примерами в скрипт.
Аргумент s — строка, содержащая примеры doctest. Строка преобразуется в скрипт Python, где примеры doctest в s преобразуются в обычный код, а всё остальное преобразуется в комментарии Python. Сгенерированный скрипт возвращается в виде строки. Например,
import doctest print doctest.script_from_examples(r""" Set x and y to 1 and 2. >>> x, y = 1, 2 Print their sum: >>> print x+y 3 """)выводит:
# Set x and y to 1 and 2. x, y = 1, 2 # # Print their sum: print x+y # Expected: ## 3
Эта функция используется внутри других функций (см. ниже), но также может быть полезна, когда вы хотите преобразовать интерактивную сессию Python в скрипт Python.
Новая в версии 2.4.
-
doctest.testsource(module, name) -
Преобразовать doctest для объекта в скрипт.
Аргумент module — объект модуля или имя модуля с точкой, содержащий объект, чьи doctest представляют интерес. Аргумент name — имя (внутри модуля) объекта с doctest, представляющего интерес. Результатом является строка, содержащая строковый документ объекта, преобразованный в скрипт Python, как описано для
script_from_examples()выше. Например, если модульa.pyсодержит функцию верхнего уровняf(), тогдаimport a, doctest print doctest.testsource(a, "a.f")
выводит скриптовый вариант документации функции
f(), с doctest, преобразованными в код, а остальное размещено в комментариях.Новая в версии 2.3.
-
doctest.debug(module, name[, pm]) -
Отладка doctest для объекта.
Аргументы module и name такие же, как и для функции
testsource()выше. Сгенерированный скрипт Python для строкового документа названного объекта записывается в временный файл, а затем этот файл запускается под управлением отладчика Python,pdb.Для локального и глобального контекстов выполнения используется поверхностная копия
module.__dict__.Необязательный аргумент pm контролирует, используется ли постобработка отладки. Если pm имеет истинное значение, файл скрипта выполняется непосредственно, и отладчик участвует только в том случае, если скрипт завершается путем поднятия необработанного исключения. Если это произойдет, то будет выполнена постобработка отладки с помощью
pdb.post_mortem(), передавая объект стека исключений из необработанного исключения. Если pm не указан или ложно, скрипт выполняется в отладчике с самого начала, передавая соответствующий вызовexecfile()вpdb.run().Новая в версии 2.3.
Изменено в версии 2.4: Добавлен аргумент pm.
-
doctest.debug_src(src[, pm][, globs]) -
Отлаживать doctest в строке.
Это аналогично функции
debug()выше, за исключением того, что строка, содержащая примеры doctest, задаётся непосредственно через аргумент src.Необязательный аргумент pm имеет то же значение, что и в функции
debug()выше.Необязательный аргумент globs предоставляет словарь для использования в качестве контекста локального и глобального выполнения. Если не указан, или
None, используется пустой словарь. При указании используется поверхностная копия словаря.Новая в версии 2.4.
Класс DebugRunner, и особые исключения, которые он может генерировать, представляют наибольший интерес для авторов фреймворков тестирования и будут здесь лишь набросаны. Подробнее см. исходный код и особенно строковый документ DebugRunner (который является doctest!).
-
class doctest.DebugRunner([checker][, verbose][, optionflags]) -
Подкласс
DocTestRunner, который генерирует исключение, как только встречается неудача. Если возникает непредвиденное исключение, возникает исключениеUnexpectedException, содержащее тест, пример и исходное исключение. Если выход не совпадает, то генерируется исключениеDocTestFailure, содержащее тест, пример и фактический выход.Для получения информации о параметрах конструктора и методах обратитесь к документации
DocTestRunnerв разделе Расширенный API.
Существует два исключения, которые могут быть подняты экземплярами DebugRunner:
-
exception doctest.DocTestFailure(test, example, got) -
Исключение, генерируемое
DocTestRunner, чтобы указать, что фактический вывод примера doctest не совпал с ожидаемым выводом. Аргументы конструктора используются для инициализации атрибутов с такими же именами.
DocTestFailure определяет следующие атрибуты:
-
DocTestFailure.test -
Объект
DocTest, который выполнялся, когда пример потерпел неудачу.
-
DocTestFailure.example -
Пример
Example, который потерпел неудачу.
-
DocTestFailure.got -
Фактический вывод примера.
-
exception doctest.UnexpectedException(test, example, exc_info) -
Исключение, поднятое
DocTestRunner, чтобы указать, что пример doctest вызвал непредвиденное исключение. Аргументы конструктора используются для инициализации атрибутов с такими же именами.
UnexpectedException определяет следующие атрибуты:
-
UnexpectedException.test -
Объект
DocTest, который выполнялся, когда пример потерпел неудачу.
-
UnexpectedException.example -
Пример
Example, который потерпел неудачу.
-
UnexpectedException.exc_info -
Кортеж, содержащий информацию об неожиданном исключении, возвращаемый
sys.exc_info().
8. Soapbox
Как упоминалось во введении, doctest вырос до трёх основных применений:
- Проверка примеров в строковых документах.
- Тестирование регрессий.
- Исполняемая документация / тестирование на основе текста.
Эти применения имеют разные требования, и важно их различать. В частности, заполнение строковых документов запутанными тестовыми случаями делает плохую документацию.
При написании строкового описания (docstring) выбирайте примеры docstring с осторожностью. В этом есть искусство, которое нужно освоить — это может быть не естественно с самого начала. Примеры должны добавлять реальную ценность к документации. Хороший пример часто стоит многих слов. Если это сделано с умом, примеры будут бесценны для ваших пользователей и окупят затраченное время многократно, по мере того как годы идут и всё меняется. Меня до сих пор поражает, как часто один из моих doctest примеров перестаёт работать после «безобидного» изменения.
Doctest также является отличным инструментом для регрессионного тестирования, особенно если вы не скупитесь на поясняющий текст. Объединяя прозу и примеры, намного проще отслеживать, что на самом деле тестируется и почему. Когда тест терпит неудачу, хорошая проза может значительно упростить выявление проблемы и способ её исправления. Конечно, вы можете написать обширные комментарии в тестировании на основе кода, но мало кто из программистов это делает. Многие обнаружили, что использование подходов doctest вместо этого приводит к гораздо более понятным тестам. Возможно, это просто потому, что doctest делает написание прозы немного проще, чем написание кода, в то время как написание комментариев в коде немного сложнее. Я думаю, это глубже, чем просто это: естественное отношение при написании теста на основе doctest заключается в том, что вы хотите объяснить тонкости вашего программного обеспечения и проиллюстрировать их примерами. Это, в свою очередь, естественным образом приводит к файлам тестов, которые начинаются с самых простых функций и логически переходят к сложностям и граничным случаям. Результатом является связный рассказ, а не набор изолированных функций, которые тестируют изолированные части функциональности, похоже, случайным образом. Это другое отношение и дает разные результаты, размывая грань между тестированием и объяснением.
Регрессионное тестирование лучше всего ограничить выделенными объектами или файлами. Существует несколько вариантов организации тестов:
- Напишите текстовые файлы, содержащие тестовые случаи в качестве интерактивных примеров, и протестируйте файлы с помощью
testfile()илиDocFileSuite(). Это рекомендуется, хотя это проще всего сделать для новых проектов, разработанных с самого начала для использования doctest. - Определите функции с именем
_regrtest_topic, состоящие из одного строкового описания (docstring), содержащего тестовые случаи для названных тем. Эти функции можно включить в тот же файл, что и модуль, или вынести в отдельный тестовый файл. - Определите словарь
__test__, сопоставляющий темы регрессионного тестирования со строковыми описаниями (docstring), содержащими тестовые случаи.
Когда вы разместили свои тесты в модуле, сам модуль может быть исполнителем тестов. Когда тест терпит неудачу, вы можете настроить исполнителя тестов на повторный запуск только неудачного doctest во время отладки проблемы. Вот минимальный пример такого исполнителя тестов:
if __name__ == '__main__':
import doctest
flags = doctest.REPORT_NDIFF|doctest.REPORT_ONLY_FIRST_FAILURE
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/2.7/library/doctest.html