doctest — Тестирование интерактивных примеров Python
Исходный код: Lib/doctest.py
Модуль doctest ищет фрагменты текста, похожие на интерактивные сеансы Python, и выполняет эти сеансы, чтобы проверить, работают ли они точно так, как показано. Существует несколько распространённых способов использования doctest:
- Проверка актуальности документации модуля путём подтверждения того, что все интерактивные примеры по-прежнему работают так, как задокументировано.
- Выполнение регрессионного тестирования путём проверки того, что интерактивные примеры из файла теста или объекта теста работают как ожидается.
- Написание учебной документации для пакета, обильно проиллюстрированной примерами ввода-вывода. В зависимости от того, примеры или пояснительный текст акцентированы больше, это имеет характер «обучающего тестирования» или «выполняемой документации».
Вот полный, но небольшой, пример модуля:
"""
This is the "example" module.
The example module supplies one function, factorial(). For example,
>>> factorial(5)
120
"""
def factorial(n):
"""Return the factorial of n, an exact integer >= 0.
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> factorial(30)
265252859812191058636308480000000
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n must be >= 0
Factorials of floats are OK, but the float must be an exact integer:
>>> factorial(30.1)
Traceback (most recent call last):
...
ValueError: n must be exact integer
>>> factorial(30.0)
265252859812191058636308480000000
It must also not be ridiculously large:
>>> factorial(1e100)
Traceback (most recent call last):
...
OverflowError: n too large
"""
import math
if not n >= 0:
raise ValueError("n must be >= 0")
if math.floor(n) != n:
raise ValueError("n must be exact integer")
if n+1 == n: # catch a value like 1e300
raise OverflowError("n too large")
result = 1
factor = 2
while factor <= n:
result *= factor
factor += 1
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
Если вы запустите example.py напрямую из командной строки, doctest сотворит свою магию:
$ python example.py $
Вывода нет! Это нормально, и это означает, что все примеры работали. Передайте -v скрипту, и doctest выведет подробный журнал того, что он пытается сделать, и выведет сводку в конце:
$ python example.py -v
Trying:
factorial(5)
Expecting:
120
ok
Trying:
[factorial(n) for n in range(6)]
Expecting:
[1, 1, 2, 6, 24, 120]
ok
И так далее, в конечном итоге завершаясь:
Trying:
factorial(1e100)
Expecting:
Traceback (most recent call last):
...
OverflowError: n too large
ok
2 items passed all tests:
1 tests in __main__
8 tests in __main__.factorial
9 tests in 2 items.
9 passed and 0 failed.
Test passed.
$
Это всё, что вам нужно знать, чтобы начать продуктивно использовать doctest! Приступайте. В следующих разделах приводится полная информация. Обратите внимание, что в стандартном наборе тестов и библиотеках Python есть много примеров doctest. Особенно полезные примеры можно найти в стандартном файле тестов Lib/test/test_doctest.py.
Простое использование: проверка примеров в строках документации
Самый простой способ начать использование doctest (но не обязательно тот, которым вы будете продолжать пользоваться) — завершить каждый модуль M следующим образом:
if __name__ == "__main__":
import doctest
doctest.testmod()
doctest затем проверяет строки документации в модуле M.
Запуск модуля как скрипта приводит к выполнению и проверке примеров в строках документации:
python M.py
Это ничего не отобразит, если пример не пройдёт, в этом случае не пройденный(ые) пример(ы) и причина(ы) сбоя(ов) будут выведены в стандартный вывод, а последняя строка вывода будет ***Test Failed*** N failures., где N — количество примеров, которые не прошли.
Запустите его с переключателем -v вместо этого:
python M.py -v
и будет напечатан подробный отчёт обо всех проведённых тестах, а также различные сводки в конце.
Вы можете принудительно включить подробный режим, передав verbose=True в testmod(), или запретить его, передав verbose=False. В любом из этих случаев sys.argv не будет проверяться testmod() (поэтому передача -v или её отсутствие не повлияет).
Также существует сокращённая команда для запуска testmod(). Вы можете указать интерпретатору Python запустить модуль doctest непосредственно из стандартной библиотеки и передать имена модулей в командной строке:
python -m doctest -v example.py
Это импортирует example.py как автономный модуль и запустит testmod() на нём. Обратите внимание, что это может не сработать правильно, если файл является частью пакета и импортирует другие подмодули из этой папки.
Для получения дополнительной информации о testmod() см. раздел Основные API.
Простое использование: проверка примеров в текстовом файле
Другое простое применение doctest — тестирование интерактивных примеров в текстовом файле. Это можно сделать с помощью функции testfile():
import doctest
doctest.testfile("example.txt")
Этот короткий скрипт выполняет и проверяет все интерактивные примеры Python, содержащиеся в файле example.txt. Содержимое файла обрабатывается так, как если бы это была одна большая строка документации; файл не обязан содержать программу Python! Например, возможно, example.txt содержит следующее:
The ``example`` module
======================
Using ``factorial``
-------------------
This is an example text file in reStructuredText format. First import
``factorial`` from the ``example`` module:
>>> from example import factorial
Now use it:
>>> factorial(6)
120
Запуск doctest.testfile("example.txt") затем обнаруживает ошибку в этой документации:
File "./example.txt", line 14, in example.txt
Failed example:
factorial(6)
Expected:
120
Got:
720
Как и в случае с testmod(), testfile() ничего не отобразит, если пример не пройдёт. Если пример не пройдёт, то не пройденный(ые) пример(ы) и причина(ы) сбоя(ов) будут выведены в стандартный вывод, используя тот же формат, что и testmod().
По умолчанию testfile() ищет файлы в каталоге вызывающего модуля. См. раздел Основные API для описания необязательных аргументов, которые могут быть использованы для поиска файлов в других местах.
Как и testmod(), уровень подробности testfile() можно установить с помощью переключателя командной строки -v или с помощью необязательного ключевого аргумента verbose.
Также существует сокращённая команда для запуска testfile(). Вы можете указать интерпретатору Python запустить модуль doctest непосредственно из стандартной библиотеки и передать имена файлов в командной строке:
python -m doctest -v example.txt
Поскольку имя файла не заканчивается на .py, doctest предполагает, что он должен быть запущен с testfile(), а не с testmod().
Для получения дополнительной информации о testfile() см. раздел Основные API.
Как это работает
В этом разделе подробно рассматривается, как работает doctest: какие строки документации он анализирует, как находит интерактивные примеры, какой контекст выполнения использует, как обрабатывает исключения и как можно использовать флаги опций для управления его поведением. Эта информация необходима для написания примеров doctest; для информации о фактическом запуске doctest на этих примерах см. следующие разделы.
Какие строки документации проверяются?
Проверяются строка документации модуля и все строки документации функций, классов и методов. Объекты, импортированные в модуль, не проверяются.
Кроме того, если M.__test__ существует и «истина», то это должен быть словарь, и каждое значение отображает имя (строку) на объект функции, объект класса или строку. Строки документации функций и объектов классов, найденные из M.__test__, проверяются, а строки обрабатываются так, как если бы они были строками документации. В выводе ключ K в M.__test__ появляется с именем
<name of M>.__test__.K
Любые найденные классы аналогично проверяются рекурсивно, чтобы проверить строки документации в содержащихся в них методах и вложенных классах.
Деталь реализации CPython: До версии 3.4 расширяемые модули, написанные на C, не проверялись полностью doctest.
Как распознаются примеры в строках документации?
В большинстве случаев копирование и вставка сеанса интерактивной консоли работает нормально, но doctest не пытается точно эмулировать конкретную оболочку Python.
>>> # comments are ignored
>>> x = 12
>>> x
12
>>> if x == 13:
... print("yes")
... else:
... print("no")
... print("NO")
... print("NO!!!")
...
no
NO
NO!!!
>>>
Любой ожидаемый вывод должен непосредственно следовать за последней строкой кода с '>>> ' или '... ', и ожидаемый вывод (если таковой имеется) продолжается до следующей строки '>>> ' или до строки со всеми пробелами.
Подробности:
- Ожидаемый вывод не может содержать строку со всеми пробелами, так как такая строка считается сигналом окончания ожидаемого вывода. Если ожидаемый вывод содержит пустую строку, вставьте
<BLANKLINE>в пример doctest в каждом месте, где ожидается пустая строка. - Все жесткие табуляции расширяются до пробелов с использованием 8-колонных остановок табуляции. Табуляции в выводе, сгенерированном проверяемым кодом, не изменяются. Поскольку все жесткие табуляции в образце вывода расширяются, это означает, что если код вывода включает жесткие табуляции, единственный способ, которым doctest может пройти, заключается в том, что включена опция
NORMALIZE_WHITESPACEили директива. В качестве альтернативы, тест можно переписать, чтобы захватить вывод и сравнить его с ожидаемым значением в рамках теста. Такое обращение с табуляциями в исходном коде было получено путем проб и ошибок и оказалось наименее подверженным ошибкам способом обработки табуляций. Можно использовать другой алгоритм обработки табуляций, написав пользовательский классDocTestParser. - Вывод в stdout захватывается, но вывод в stderr нет (обработка исключений захватывается другим способом).
-
Если вы продолжаете строку с помощью обратного слеша в интерактивной сессии или по любой другой причине используете обратный слэш, вы должны использовать сырую строку документации, которая сохранит ваши обратные слэши точно так, как вы их набираете:
>>> def f(x): ... r'''Backslashes in a raw docstring: m\n''' >>> print(f.__doc__) Backslashes in a raw docstring: m\n
В противном случае обратный слэш будет интерпретироваться как часть строки. Например,
\nвыше будет интерпретироваться как символ новой строки. В качестве альтернативы, вы можете удвоить каждый обратный слэш в версии doctest (и не использовать сырую строку):>>> def f(x): ... '''Backslashes in a raw docstring: m\\n''' >>> print(f.__doc__) Backslashes in a raw docstring: m\n
-
Начальная колонка не имеет значения:
>>> assert "Easy!" >>> import math >>> math.floor(1.9) 1и столько же ведущих пробельных символов удаляются из ожидаемого вывода, сколько было в начальной строке
'>>> ', которая начала пример.
Контекст выполнения
По умолчанию каждый раз, когда doctest находит строку документации для проверки, он использует поверхностную копию глобальных переменных M, чтобы запуск тестов не изменял реальные глобальные переменные модуля и чтобы один тест в M не оставлял следов, которые случайно позволяли бы другому тесту работать. Это означает, что примеры могут свободно использовать любые имена, определенные на верхнем уровне в M, и имена, определенные ранее в строке документации, которая выполняется. Примеры не могут видеть имена, определенные в других строках документации.
Вы можете принудительно использовать свой собственный словарь в качестве контекста выполнения, передав globs=your_dict в testmod() или testfile().
Что насчёт исключений?
Без проблем, при условии, что трассировка стека является единственным выводом, производимым примером: просто вставьте трассировку стека. 1 Поскольку трассировки содержат детали, которые, вероятно, будут быстро меняться (например, точные пути к файлам и номера строк), это один из случаев, когда doctest прилагает усилия, чтобы быть гибким в отношении того, что он принимает.
Простой пример:
>>> [1, 2, 3].remove(42) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: list.remove(x): x not in list
Этот doctest выполняется, если возникает ValueError со значением list.remove(x):
x not in list , как показано.
Ожидаемый вывод для исключения должен начинаться с заголовка трассировки стека, который может быть любой из следующих двух строк, отступы которых совпадают с первым отступом строки примера:
Traceback (most recent call last): Traceback (innermost last):
Заголовок трассировки стека следует за необязательным стеком трассировки, содержимое которого игнорируется doctest. Стек трассировки обычно опускается или копируется дословно из интерактивной сессии.
За стеком трассировки следует наиболее интересная часть: строка(и), содержащая тип и детали исключения. Это обычно последняя строка трассировки, но может занимать несколько строк, если у исключения есть многострочные детали:
>>> raise ValueError('multi\n line\ndetail')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: multi
line
detail
Последние три строки (начиная с ValueError) сравниваются с типом и деталями исключения, а остальное игнорируется.
Лучшей практикой является опускание стека трассировки, если он не добавляет существенной ценности к примеру. Таким образом, последний пример, вероятно, будет лучше следующим:
>>> raise ValueError('multi\n line\ndetail')
Traceback (most recent call last):
...
ValueError: multi
line
detail
Обратите внимание, что трассировки обрабатываются очень особым образом. В частности, в переписанном примере использование ... не зависит от опции doctest ELLIPSIS. Эллипсис в этом примере можно опустить, или же это могут быть три (или триста) запятых или цифр, или отформатированный текст из скетча Монти Пайтона.
Некоторые детали, которые вы должны прочитать один раз, но не нужно запоминать:
- Doctest не может догадаться, получен ли ожидаемый вывод из трассировки стека исключения или из обычного вывода. Таким образом, например, пример, который ожидает
ValueError: 42 is prime, пройдет, независимо от того, возникает лиValueErrorили пример просто выводит этот текст трассировки. На практике обычный вывод редко начинается с строки заголовка трассировки, поэтому это не создает реальных проблем. - Каждая строка стека трассировки (если присутствует) должна быть отступа более глубоко, чем первая строка примера, или начинаться с символа, не являющегося буквой или цифрой. Первая строка, следующая за заголовком трассировки, отступаемая одинаково и начинающаяся с буквы или цифры, считается началом деталей исключения. Разумеется, это работает правильно для реальных трассировок.
- Когда опция doctest
IGNORE_EXCEPTION_DETAILуказана, всё, что следует за левым двоеточием и любой информацией о модуле в имени исключения, игнорируется. - Интерактивная оболочка опускает строку заголовка трассировки для некоторых
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
Флаги параметров
Несколько флагов параметров контролируют различные аспекты поведения doctest. Символьные имена флагов предоставляются в виде констант модуля, которые могут быть побитово сложены вместе и переданы различным функциям. Эти имена также могут быть использованы в директивах doctest и могут быть переданы в командную строку doctest через параметр -o.
Новое в версии 3.4: Параметр командной строки -o.
Первая группа параметров определяет семантику теста, контролируя аспекты того, как doctest определяет, соответствует ли фактический вывод ожидаемому выводу примера:
-
doctest.DONT_ACCEPT_TRUE_FOR_1 -
По умолчанию, если блок ожидаемого вывода содержит только
1, блок фактического вывода, содержащий только1или толькоTrue, считается совпадающим, и аналогично для0противFalse. КогдаDONT_ACCEPT_TRUE_FOR_1указан, ни одна подстановка не допускается. По умолчанию поведение учитывает то, что Python изменил тип возвращаемого значения многих функций с целого на булево; doctest, ожидающие «целое число» в выводе, все еще работают в этих случаях. Этот параметр, вероятно, исчезнет, но не в ближайшие несколько лет.
-
doctest.DONT_ACCEPT_BLANKLINE -
По умолчанию, если блок ожидаемого вывода содержит строку, содержащую только строку
<BLANKLINE>, эта строка будет совпадать с пустой строкой в фактическом выводе. Поскольку фактически пустая строка ограничивает ожидаемый вывод, это единственный способ указать, что ожидается пустая строка. КогдаDONT_ACCEPT_BLANKLINEуказан, эта подстановка не допускается.
-
doctest.NORMALIZE_WHITESPACE -
При указании все последовательности пробелов (пробелы и символы новой строки) обрабатываются как равные. Любая последовательность пробелов в ожидаемом выводе будет соответствовать любой последовательности пробелов в фактическом выводе. По умолчанию пробелы должны точно совпадать.
NORMALIZE_WHITESPACEособенно полезен, когда строка ожидаемого вывода очень длинная, и вы хотите заключить ее на несколько строк в исходном коде.
-
doctest.ELLIPSIS -
При указании маркер многоточия (
...) в ожидаемом выводе может соответствовать любому подстроке в фактическом выводе. Это включает подстроки, охватывающие границы строк, и пустые подстроки, поэтому лучше всего использовать это просто. Сложные использования могут привести к тем же «опс, это совпало слишком много!» неожиданностям, которым.*подвержен в регулярных выражениях.
-
doctest.IGNORE_EXCEPTION_DETAIL -
При указании пример, который ожидает исключение, проходит, если возникает исключение ожидаемого типа, даже если подробности исключения не совпадают. Например, пример, ожидающий
ValueError: 42, пройдет, если фактически поднято исключениеValueError: 3*14, но потерпит неудачу, например, еслиTypeErrorподнято.Он также проигнорирует имя модуля, используемое в сообщениях doctest Python 3. Таким образом, оба варианта будут работать с указанным флагом, независимо от того, выполняется ли тест под Python 2.7 или Python 3.2 (или более поздних версий):
>>> raise CustomError('message') Traceback (most recent call last): CustomError: message >>> raise CustomError('message') Traceback (most recent call last): my_module.CustomError: messageОбратите внимание, что
ELLIPSISтакже можно использовать для игнорирования деталей сообщения об ошибке, но такой тест все равно может потерпеть неудачу, в зависимости от того, печатаются ли детали модуля как часть имени ошибки. ИспользованиеIGNORE_EXCEPTION_DETAILи подробностей из Python 2.3 также является единственным ясным способом написать doctest, которому не важны детали исключения, но который продолжает проходить под Python 2.3 и ранее (эти версии не поддерживают директивы doctest и игнорируют их как не относящиеся к делу комментарии). Например:>>> (1, 2)[3] = 'moo' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: object doesn't support item assignment
проходит под Python 2.3 и более поздними версиями Python с указанным флагом, хотя подробности изменились в Python 2.4 на «не» вместо «не».
Изменено в версии 3.2:
IGNORE_EXCEPTION_DETAILтеперь также игнорирует любую информацию, относящуюся к модулю, содержащему тестируемое исключение.
-
doctest.SKIP -
При указании пример вообще не запускается. Это может быть полезно в контекстах, где примеры doctest служат как документацией, так и тестовыми случаями, и пример должен быть включен в документацию, но не должен проверяться. Например, вывод примера может быть случайным; или пример может зависеть от ресурсов, которые недоступны для драйвера теста.
Флаг SKIP также может быть использован для временного «комментирования» примеров.
-
doctest.COMPARISON_FLAGS -
Маска битов, объединяющая все флаги сравнения выше.
Вторая группа параметров контролирует, как сообщаются о сбоях тестов:
-
doctest.REPORT_UDIFF -
При указании сбои, связанные с ожидаемым и фактическим многострочным выводом, отображаются с помощью объединённой разницы.
-
doctest.REPORT_CDIFF -
При указании сбои, связанные с ожидаемым и фактическим многострочным выводом, будут отображаться с помощью контекстной разницы.
-
doctest.REPORT_NDIFF -
При указании различия вычисляются с помощью
difflib.Differ, используя тот же алгоритм, что и популярная утилитаndiff.py. Это единственный метод, который отмечает различия как внутри строк, так и между строками. Например, если строка ожидаемого вывода содержит цифру1, а фактический вывод содержит буквуl, вставляется строка с символом «^», обозначающим позиции несовпадающих столбцов.
-
doctest.REPORT_ONLY_FIRST_FAILURE -
При указании отображается первый неисправный пример в каждом doctest, но вывод для всех остальных примеров подавляется. Это предотвратит doctest от сообщения о правильных примерах, которые ломаются из-за предыдущих неудач; но также может скрыть неправильные примеры, которые терпят неудачу независимо от первого сбоя. При указании
REPORT_ONLY_FIRST_FAILURE, оставшиеся примеры все равно выполняются и все равно учитываются в общем количестве сообщенных сбоев; только вывод подавляется.
-
doctest.FAIL_FAST -
При указании выходе после первого неисправного примера и не пытайтесь выполнить оставшиеся примеры. Таким образом, число сообщенных сбоев будет не более 1. Этот флаг может быть полезным во время отладки, поскольку примеры после первого сбоя даже не будут создавать вывод отладки.
Командная строка doctest принимает опцию
-fв качестве сокращения для-o FAIL_FAST.Новое в версии 3.4.
-
doctest.REPORTING_FLAGS -
Маска битов, объединяющая все флаги отчётности выше.
Также есть способ зарегистрировать новые имена флагов параметров, хотя это не полезно, если вы не собираетесь расширять doctest внутренности с помощью наследования:
-
doctest.register_optionflag(name) -
Создать новый флаг параметра с заданным именем и вернуть целое значение нового флага.
register_optionflag()может быть использован при наследовании отOutputCheckerилиDocTestRunnerдля создания новых параметров, поддерживаемых вашими подклассами.register_optionflag()всегда должен вызываться с помощью следующего выражения:MY_FLAG = register_optionflag('MY_FLAG')
Директивы
Директивы doctest могут использоваться для изменения флагов опций для отдельного примера. Директивы doctest — это специальные комментарии Python, следующие за исходным кодом примера:
directive ::= "#" "doctest:" directive_options
directive_options ::= directive_option ("," directive_option)\*
directive_option ::= on_or_off directive_option_name
on_or_off ::= "+" \| "-"
directive_option_name ::= "DONT_ACCEPT_BLANKLINE" \| "NORMALIZE_WHITESPACE" \| ...
Пробелы не допускаются между + или - и именем опции директивы. Имя опции директивы может быть любым из имен флагов опций, описанных выше.
Директивы doctest примера изменяют поведение doctest для этого единственного примера. Используйте + для включения указанного поведения или - для его отключения.
Например, этот тест проходит:
>>> print(list(range(20))) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Без директивы он не пройдёт, как из-за отсутствия двух пробелов перед однозначными элементами списка в фактическом выводе, так и из-за того, что фактический вывод находится на одной строке. Этот тест также проходит, и для этого также требуется директива:
>>> print(list(range(20))) [0, 1, ..., 18, 19]
Можно использовать несколько директив в одной физической строке, разделенных запятыми:
>>> print(list(range(20))) [0, 1, ..., 18, 19]
Если для одного примера используются несколько комментариев с директивами, то они объединяются:
>>> print(list(range(20))) ... [0, 1, ..., 18, 19]
Как показывает предыдущий пример, можно добавлять ... строки в свой пример, содержащие только директивы. Это может быть полезно, когда пример слишком длинный, чтобы директива удобно поместилась в той же строке:
>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40))) ... [0, ..., 4, 10, ..., 19, 30, ..., 39]
Обратите внимание, что поскольку все опции по умолчанию отключены, а директивы применяются только к тому примеру, в котором они появляются, включение опций (через + в директиве) обычно является единственным осмысленным выбором. Однако флаги опций также могут передаваться в функции, которые запускают doctests, устанавливая различные значения по умолчанию. В таких случаях отключение опции с помощью - в директиве может быть полезно.
Предупреждения
doctest серьёзно относится к требованию точного соответствия в ожидаемом выводе. Если даже один символ не совпадёт, тест завершится неудачей. Это, вероятно, вас несколько раз удивит, когда вы узнаете, что именно Python гарантирует и не гарантирует относительно вывода. Например, при печати множества Python не гарантирует, что элементы будут напечатаны в каком-либо определённом порядке, поэтому тест, подобный
>>> foo()
{"Hermione", "Harry"}
уязвим! Одним из решений является использование
>>> foo() == {"Hermione", "Harry"}
True
Другое решение —
>>> d = sorted(foo()) >>> d ['Harry', 'Hermione']
Примечание
До Python 3.6 при печати словаря Python не гарантировал, что пары ключ-значение будут напечатаны в каком-либо определённом порядке.
Есть и другие, но вы поняли суть.
Ещё одна плохая идея — печатать вещи, содержащие адрес объекта, например
>>> id(1.0) # certain to fail some of the time 7948648 >>> class C: pass >>> C() # the default repr() for instances embeds an address <__main__.C instance at 0x00AC18F0>
Директива ELLIPSIS предлагает хорошее решение для последнего примера:
>>> C() <__main__.C instance at 0x...>
Вещественные числа также подвержены небольшим вариациям вывода на разных платформах, поскольку Python делегирует форматирование чисел с плавающей точкой платформенной библиотеке C, а качество таких библиотек сильно различается.
>>> 1./7 # risky 0.14285714285714285 >>> print(1./7) # safer 0.142857142857 >>> print(round(1./7, 6)) # much safer 0.142857
Числа в формате I/2.**J безопасны на всех платформах, и я часто составляю примеры doctest, чтобы получить числа в этом формате:
>>> 3./4 # utterly safe 0.75
Простые дроби также легче понять людям, и это способствует улучшению документации.
Основной API
Функции testmod() и testfile() предоставляют простой интерфейс к doctest, который должен быть достаточным для большинства основных применений. Для менее формального введения в эти две функции см. разделы Простое использование: Проверка примеров в строках документации и Простое использование: Проверка примеров в текстовом файле.
-
doctest.testfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, parser=DocTestParser(), encoding=None) -
Все аргументы, кроме filename, являются необязательными и должны быть указаны в ключевом формате.
Проверка примеров в файле с именем filename. Возвращает
(failure_count, test_count).Необязательный аргумент module_relative указывает, как следует интерпретировать имя файла:
- Если module_relative равен
True(по умолчанию), то filename задаёт независимый от операционной системы путь, относящийся к модулю. По умолчанию этот путь относится к каталогу вызывающего модуля; но если указан аргумент package, то он относится к этому пакету. Чтобы обеспечить независимость от операционной системы, filename должен использовать/-символы для разделения сегментов пути и не должен быть абсолютным путём (то есть не может начинаться с/). - Если module_relative равен
False, то filename задаёт путь, специфичный для операционной системы. Путь может быть абсолютным или относительным; относительные пути разрешаются относительно текущего рабочего каталога.
Необязательный аргумент name задаёт имя теста; по умолчанию или если
None, используетсяos.path.basename(filename).Необязательный аргумент package — это пакет Python или имя пакета Python, каталог которого должен использоваться в качестве базового каталога для имени файла, относящегося к модулю. Если пакет не указан, то каталог вызывающего модуля используется в качестве базового каталога для имён файлов, относящихся к модулю. Указание package при module_relative, равном
False, является ошибкой.Необязательный аргумент globs предоставляет словарь, который будет использоваться в качестве глобальных переменных при выполнении примеров. Для doctest создаётся новая поверхностная копия этого словаря, поэтому его примеры начинаются с чистого листа. По умолчанию или если
None, используется новый пустой словарь.Необязательный аргумент extraglobs предоставляет словарь, который сливается с глобальными переменными, используемыми для выполнения примеров. Это работает аналогично
dict.update(): если globs и extraglobs имеют общие ключи, соответствующее значение в extraglobs появляется в объединённом словаре. По умолчанию или еслиNone, дополнительные глобальные переменные не используются. Это расширенная функция, которая позволяет параметризовать doctest. Например, doctest можно написать для базового класса, используя общее имя для класса, а затем повторно использовать для тестирования любого количества подклассов путём передачи словаря extraglobs, сопоставляющего общее имя с тестируемым подклассом.Необязательный аргумент verbose выводит много информации, если true, и выводит только ошибки, если false; по умолчанию или если
None, он true только тогда, когда'-v'содержится вsys.argv.Необязательный аргумент report выводит сводку в конце, если true, иначе ничего не выводит. В режиме verbose сводка подробная, иначе сводка очень краткая (на самом деле пустая, если все тесты пройдены).
Необязательный аргумент optionflags (значение по умолчанию 0) принимает побитовое ИЛИ флагов опций. См. раздел Флаги опций.
Необязательный аргумент raise_on_error по умолчанию false. Если true, при первой ошибке или непредвиденном исключении в примере поднимается исключение. Это позволяет отлаживать ошибки с помощью post-mortem. По умолчанию выполнение примеров продолжается.
Необязательный аргумент parser указывает
DocTestParser(или подкласс), который должен использоваться для извлечения тестов из файлов. По умолчанию используется обычный парсер (т. е.,DocTestParser()).Необязательный аргумент encoding задаёт кодировку, которая должна использоваться для преобразования файла в unicode.
- Если module_relative равен
-
doctest.testmod(m=None, name=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, exclude_empty=False) -
Все аргументы необязательные, все кроме m должны быть указаны в ключевом формате.
Проверка примеров в строках документации в функциях и классах, доступных из модуля m (или модуля
__main__, если m не указан или равенNone), начиная сm.__doc__.Также проверяются примеры, доступные из словаря
m.__test__, если он существует и неNone.m.__test__сопоставляет имена (строки) с функциями, классами и строками; для функций и классов проверяются строки документации; для строк проверяются строки напрямую, как если бы они были строками документации.Проверяются только строки документации объектов, принадлежащих модулю m.
Возвращает
(failure_count, test_count).Необязательный аргумент name задаёт имя модуля; по умолчанию или если
None, используетсяm.__name__.Необязательный аргумент exclude_empty по умолчанию false. Если true, объекты, для которых не найдены тесты doctest, исключаются из рассмотрения. По умолчанию — это откат назад для совместимости, чтобы код, всё ещё использующий
doctest.master.summarize()в сочетании сtestmod(), по-прежнему получал вывод для объектов без тестов. Аргумент exclude_empty в новом конструктореDocTestFinderпо умолчанию равен true.Необязательные аргументы extraglobs, verbose, report, optionflags, raise_on_error и globs аналогичны аргументам функции
testfile()выше, за исключением того, что globs по умолчаниюm.__dict__.
-
doctest.run_docstring_examples(f, globs, verbose=False, name="NoName", compileflags=None, optionflags=0) -
Проверка примеров, связанных с объектом f; например, f может быть строкой, модулем, функцией или объектом класса.
Для контекста выполнения используется поверхностная копия словаря аргумента globs.
Необязательный аргумент name используется в сообщениях об ошибках и по умолчанию равен
"NoName".Если необязательный аргумент verbose равен true, вывод генерируется даже в случае отсутствия ошибок. По умолчанию вывод генерируется только при наличии ошибки в примере.
Необязательный аргумент compileflags задаёт набор флагов, которые должен использовать компилятор Python при выполнении примеров. По умолчанию или если
None, вычисляются флаги, соответствующие набору будущих функций, найденных в globs.Необязательный аргумент optionflags работает так же, как для функции
testfile()выше.
API модуля Unittest
По мере роста вашей коллекции модулей с тестами doctest, вам понадобится способ их систематического запуска. doctest предоставляет две функции, которые можно использовать для создания наборов тестов unittest из модулей и текстовых файлов, содержащих doctest. Для интеграции с обнаружением тестов unittest, включите функцию load_tests() в ваш тестовый модуль:
import unittest
import doctest
import my_module_with_doctests
def load_tests(loader, tests, ignore):
tests.addTests(doctest.DocTestSuite(my_module_with_doctests))
return tests
Существуют две основные функции для создания экземпляров unittest.TestSuite из текстовых файлов и модулей с doctest:
-
doctest.DocFileSuite(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None) -
Преобразование тестов doctest из одного или нескольких текстовых файлов в
unittest.TestSuite.Возвращаемый
unittest.TestSuiteдолжен запускаться фреймворком unittest и выполняет интерактивные примеры в каждом файле. Если пример в любом файле завершается ошибкой, то составленный тестовый кейс завершается ошибкой, и возникает исключениеfailureException, показывающее имя файла с тестом и (иногда приблизительный) номер строки.Передайте один или несколько путей (в виде строк) к текстовым файлам для проверки.
Опции могут быть заданы в качестве именованных аргументов:
Необязательный аргумент module_relative указывает, как следует интерпретировать имена файлов в paths:
- Если module_relative имеет значение
True(по умолчанию), то каждое имя файла в paths задаёт независимый от ОС путь, относительный к модулю. По умолчанию этот путь относительный к каталогу вызывающего модуля; но если указан аргумент package, то он относительный к этому пакету. Для обеспечения независимости от ОС каждый имя файла должно использовать символы/для разделения сегментов пути, и не должно быть абсолютным путём (т. е., оно не может начинаться с/). - Если module_relative имеет значение
False, то каждое имя файла в paths задаёт путь, зависящий от ОС. Путь может быть абсолютным или относительным; относительные пути разрешаются относительно текущей рабочей директории.
Необязательный аргумент package является пакетом Python или именем пакета Python, каталог которого должен использоваться в качестве базового каталога для имен модульных файлов в paths. Если пакет не указан, то каталогом по умолчанию является каталог вызывающего модуля. Указание аргумента package является ошибкой, если module_relative имеет значение
False.Необязательный аргумент setUp определяет функцию подготовки для тестового набора. Она вызывается перед запуском тестов в каждом файле. Функция setUp будет получать объект
DocTest. Функция setUp может получить доступ к глобальным переменным теста через атрибут globs переданного теста.Необязательный аргумент tearDown определяет функцию завершения для тестового набора. Она вызывается после запуска тестов в каждом файле. Функция tearDown будет получать объект
DocTest. Функция setUp может получить доступ к глобальным переменным теста через атрибут globs переданного теста.Необязательный аргумент globs — словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создаётся новая копия этого словаря. По умолчанию globs — новый пустой словарь.
Необязательный аргумент optionflags задаёт значения по умолчанию для опций doctest для тестов, созданных путём побитового или объединения отдельных флагов. См. раздел Флаги опций. См. функцию
set_unittest_reportflags()ниже для лучшего способа настройки опций отчёта.Необязательный аргумент parser указывает
DocTestParser(или подкласс), который следует использовать для извлечения тестов из файлов. По умолчанию используется обычный парсер (т.е.,DocTestParser()).Необязательный аргумент encoding указывает кодировку, которая должна использоваться для преобразования файла в unicode.
Глобальная переменная
__file__добавляется к глобальным переменным, предоставляемым doctest, загруженным из текстового файла с использованиемDocFileSuite(). - Если module_relative имеет значение
-
doctest.DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None) -
Преобразует тесты doctest для модуля в
unittest.TestSuite.Возвращаемый
unittest.TestSuiteдолжен запускаться фреймворком unittest и выполняет каждый doctest в модуле. Если какой-либо из doctest завершается ошибкой, то составленный тестовый кейс завершается ошибкой, и возникает исключениеfailureException, показывающее имя файла с тестом и (иногда приблизительный) номер строки.Необязательный аргумент module задаёт модуль для тестирования. Это может быть объект модуля или (возможно, с точками) имя модуля. Если не указано, используется вызывающий эту функцию модуль.
Необязательный аргумент globs — словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создаётся новая копия этого словаря. По умолчанию globs — новый пустой словарь.
Необязательный аргумент extraglobs задаёт дополнительный набор глобальных переменных, который объединяется со словарем globs. По умолчанию дополнительные глобальные переменные не используются.
Необязательный аргумент test_finder — объект
DocTestFinder(или его замена), который используется для извлечения doctest из модуля.Необязательные аргументы setUp, tearDown и optionflags такие же, как и для функции
DocFileSuite()выше.Функция использует ту же технику поиска, что и
testmod().Изменено в версии 3.5:
DocTestSuite()возвращает пустойunittest.TestSuite, если module не содержит строк документации, вместо того, чтобы подниматьValueError.
Внутри DocTestSuite() создаёт unittest.TestSuite из экземпляров doctest.DocTestCase, а DocTestCase — подкласс unittest.TestCase. DocTestCase здесь не документируется (это внутренняя деталь), но изучение его кода может ответить на вопросы об интеграции с фреймворком unittest.
Аналогично, DocFileSuite() создаёт unittest.TestSuite из экземпляров doctest.DocFileCase, а DocFileCase является подклассом DocTestCase.
Таким образом, оба способа создания unittest.TestSuite запускают экземпляры DocTestCase. Это важно по одной причине: когда вы запускаете функции doctest самостоятельно, вы можете управлять параметрами doctest, передавая флаги опций функциям doctest. Однако, если вы пишете фреймворк unittest, то фреймворк unittest в конечном счёте управляет тем, когда и как выполняются тесты. Автор фреймворка обычно хочет управлять параметрами отчёта doctest (например, заданными опциями командной строки), но нет способа передать опции через unittest к исполнителям тестов doctest.
По этой причине doctest также поддерживает понятие флагов отчёта doctest, специфичных для поддержки фреймворка unittest, через эту функцию:
-
doctest.set_unittest_reportflags(flags) -
Установите флаги отчёта
doctestдля использования.Аргумент flags принимает побитовое ИЛИ флагов параметров. См. раздел Флаги параметров. Можно использовать только «флаги отчёта».
Это настройка на уровне модуля, которая влияет на все будущие тесты doctest, выполняемые модулем
unittest: методrunTest()экземпляраDocTestCaseпроверяет флаги параметров, указанные для тестового случая при создании экземпляраDocTestCase. Если не были указаны флаги отчёта (что является типичным и ожидаемым случаем), флаги отчётаdoctest’sunittestпобитово объединяются с флагами параметров, а дополненные флаги параметров передаются экземпляруDocTestRunner, созданному для запуска doctest. Если какие-либо флаги отчёта были указаны при создании экземпляраDocTestCase, флаги отчётаdoctest’sunittestигнорируются.Значение флагов отчёта
unittest, действующее перед вызовом функции, возвращается функцией.
Расширенный API
Базовый API представляет собой простой оболочку, предназначенный для удобного использования doctest. Он достаточно гибкий и должен удовлетворить потребности большинства пользователей; однако, если вам требуется более точный контроль над тестированием или вы хотите расширить возможности doctest, следует использовать расширенный API.
Расширенный API вращается вокруг двух контейнерных классов, используемых для хранения интерактивных примеров, извлечённых из тестовых случаев doctest:
-
Example: Один Python-оператор, совместно с ожидаемым результатом. -
DocTest: КоллекцияExampleэлементов, обычно извлекаемых из одной строки документации или текстового файла.
Для поиска, анализа, выполнения и проверки примеров doctest определены дополнительные обработчики классов:
-
DocTestFinder: Находит все строки документации в заданном модуле и используетDocTestParserдля созданияDocTestиз каждой строки документации, содержащей интерактивные примеры. -
DocTestParser: Создаёт объектDocTestиз строки (например, из строки документации объекта). -
DocTestRunner: Выполняет примеры вDocTestи используетOutputCheckerдля проверки их результата. -
OutputChecker: Сравнивает фактический результат примера doctest с ожидаемым результатом и определяет, совпадают ли они.
Взаимосвязь между этими классами обработки суммируется в следующей диаграмме:
list of:
+------+ +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+ | ^ +---------+ | ^ (printed)
| | | Example | | |
v | | ... | v |
DocTestParser | Example | OutputChecker
+---------+
Объекты DocTest
-
class doctest.DocTest(examples, globs, name, filename, lineno, docstring) -
Коллекция примеров doctest, которые следует выполнить в одном пространстве имён. Аргументы конструктора используются для инициализации атрибутов с такими же именами.
DocTestопределяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.-
examples -
Список объектов
Example, кодирующих отдельные интерактивные примеры Python, которые должны быть выполнены этим тестом.
-
globs -
Пространство имён (также известное как глобальные переменные), в котором должны выполняться примеры. Это словарь, сопоставляющий имена со значениями. Любые изменения в пространстве имён, произведённые примерами (например, привязка новых переменных), будут отражены в
globsпосле выполнения теста.
-
name -
Строка имени, идентифицирующая
DocTest. Обычно это имя объекта или файла, из которого был извлечён тест.
-
filename -
Имя файла, из которого был извлечён этот
DocTest; илиNone, если имя файла неизвестно илиDocTestне был извлечён из файла.
-
lineno -
Номер строки в
filename, где начинается этотDocTest, илиNone, если номер строки недоступен. Этот номер строки является нулевым относительно начала файла.
-
docstring -
Строка, из которой был извлечён тест, или
None, если строка недоступна или тест не был извлечён из строки.
-
Объекты Example
-
class doctest.Example(source, want, exc_msg=None, lineno=0, indent=0, options=None) -
Один интерактивный пример, состоящий из оператора Python и ожидаемого результата. Аргументы конструктора используются для инициализации атрибутов с такими же именами.
Exampleопределяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.-
source -
Строка, содержащая исходный код примера. Этот исходный код состоит из одного оператора Python и всегда заканчивается новой строкой; конструктор добавляет новую строку, если необходимо.
-
want -
Ожидаемый результат выполнения исходного кода примера (либо из stdout, либо обратная трассировка в случае исключения).
wantзаканчивается новой строкой, если не ожидается вывод, в противном случае это пустая строка. Конструктор добавляет новую строку, если необходимо.
-
exc_msg -
Сообщение об исключении, сгенерированное примером, если ожидается, что пример сгенерирует исключение; или
None, если не ожидается генерация исключения. Это сообщение об исключении сравнивается со значением возвратаtraceback.format_exception_only().exc_msgзаканчивается новой строкой, если это неNone. Конструктор добавляет новую строку, если нужно.
-
lineno -
Номер строки в строке, содержащей этот пример, где начинается пример. Этот номер строки является нулевым относительно начала содержащей строки.
-
indent -
Отступ примера в содержащей строке, т.е. количество символов пробела, предшествующих первому символу примера.
-
options -
Словарь, сопоставляющий флаги параметров со значениями
TrueилиFalse, используемые для переопределения параметров по умолчанию для этого примера. Любые флаги параметров, не содержащиеся в этом словаре, оставляются в их значениях по умолчанию (как указано вDocTestRunnerDocTestRunner’soptionflags). По умолчанию параметры не установлены.
-
Объекты DocTestFinder
-
class doctest.DocTestFinder(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True) -
Класс обработки, используемый для извлечения
DocTestобъектов, относящихся к данному объекту, из его строчной документации и строчной документации вложенных объектов.DocTestобъекты могут быть извлечены из модулей, классов, функций, методов, статических методов, методов класса и свойств.Необязательный аргумент verbose может использоваться для отображения объектов, просматриваемых поисковиком. По умолчанию он равен
False(нет вывода).Необязательный аргумент parser задаёт объект
DocTestParser(или его аналог), который используется для извлечения примеров из строчной документации.Если необязательный аргумент recurse равен false, то
DocTestFinder.find()будет проверять только указанный объект, а не любые вложенные объекты.Если необязательный аргумент exclude_empty равен false, то
DocTestFinder.find()будет включать тесты для объектов с пустой строчной документацией.DocTestFinderопределяет следующий метод:-
find(obj[, name][, module][, globs][, extraglobs]) -
Возвращает список
DocTestобъектов, определённых строчной документацией obj или строчной документацией любых вложенных объектов.Необязательный аргумент name задаёт имя объекта; это имя будет использоваться для построения имён возвращаемых
DocTestобъектов. Если name не указан, используетсяobj.__name__.Необязательный параметр module — это модуль, содержащий данный объект. Если модуль не указан или равен
None, поисковик тестов попытается автоматически определить правильный модуль. Модуль объекта используется:- В качестве пространства имён по умолчанию, если globs не указан.
- Для предотвращения извлечения DocTest из объектов, импортированных из других модулей. (Вложенные объекты с модулями, отличными от module, игнорируются.)
- Для поиска имени файла, содержащего объект.
- Для определения номера строки объекта в файле.
Если module равен
False, попытка найти модуль не будет предпринята. Это запутанно, используется в основном при тестировании самого doctest: если module равенFalse, или равенNoneно не может быть найден автоматически, то все объекты считаются принадлежащими несуществующему модулю, так что все вложенные объекты будут (рекурсивно) проверяться на наличие примеров.Глобальные переменные для каждого
DocTestформируются путём объединения globs и extraglobs (связанные в extraglobs переопределяют связи в globs). Для каждогоDocTestсоздаётся новая поверхностная копия словаря глобальных переменных. Если globs не указан, он по умолчанию равен __dict__ модуля, если указан, или{}в противном случае. Если extraglobs не указан, он по умолчанию равен{}.
-
Объекты DocTestParser
-
class doctest.DocTestParser -
Класс обработки, используемый для извлечения интерактивных примеров из строки и использования их для создания объекта
DocTest.DocTestParserопределяет следующие методы:-
get_doctest(string, globs, name, filename, lineno) -
Извлекает все примеры doctest из заданной строки и собирает их в объект
DocTest.globs, name, filename и lineno — атрибуты нового объекта
DocTest. Дополнительную информацию см. в документации поDocTest.
-
get_examples(string, name='<string>') -
Извлекает все примеры doctest из заданной строки и возвращает их в виде списка объектов
Example. Номера строк имеют нулевую базу. Необязательный аргумент name — это имя, идентифицирующее эту строку, используется только для сообщений об ошибках.
-
parse(string, name='<string>') -
Разделяет заданную строку на примеры и промежуточный текст и возвращает их в виде списка, чередующего объекты
Exampleи строк. Номера строк дляExampleимеют нулевую базу. Необязательный аргумент name — это имя, идентифицирующее эту строку, используется только для сообщений об ошибках.
-
Объекты DocTestRunner
-
class doctest.DocTestRunner(checker=None, verbose=None, optionflags=0) -
Класс обработки, используемый для выполнения и проверки интерактивных примеров в
DocTest.Сравнение ожидаемого и фактического вывода выполняется с помощью
OutputChecker. Это сравнение можно настроить с помощью ряда флагов опций; см. раздел Флаги опций для получения дополнительной информации. Если флагов опций недостаточно, сравнение также можно настроить, передав подклассOutputCheckerв конструктор.Вывод тестового исполнителя можно контролировать двумя способами. Во-первых, можно передать функцию вывода в
TestRunner.run(); эта функция будет вызываться со строками, которые должны быть отображены. По умолчанию используетсяsys.stdout.write. Если захват вывода недостаточен, то вывод можно также настроить, создав подкласс DocTestRunner и переопределив методыreport_start(),report_success(),report_unexpected_exception()иreport_failure().Необязательный ключевой аргумент checker указывает объект
OutputChecker(или его замену), который должен использоваться для сравнения ожидаемого вывода с фактическим выводом примеров doctest.Необязательный ключевой аргумент verbose управляет подробностью вывода
DocTestRunner. Если verbose равенTrue, то печатается информация о каждом примере во время его выполнения. Если verbose равенFalse, то выводятся только результаты неудач. Если verbose не указан или равенNone, то вывод используется только при использовании командной строки-v.Необязательный ключевой аргумент optionflags может быть использован для управления тем, как тестовый загрузчик сравнивает ожидаемый вывод с фактическим, и как он отображает результаты ошибок. Для получения дополнительной информации см. раздел Флаги опций.
DocTestParserопределяет следующие методы:-
report_start(out, test, example) -
Сообщает, что тестовый загрузчик собирается обработать данный пример. Этот метод предоставляется для того, чтобы подклассы
DocTestRunnerмогли настроить свой вывод; его не следует вызывать напрямую.example — пример, который собирается обработать. test — тест, содержащий example. out — функция вывода, переданная в
DocTestRunner.run().
-
report_success(out, test, example, got) -
Сообщает, что данный пример успешно выполнен. Этот метод предоставляется для того, чтобы подклассы
DocTestRunnerмогли настроить свой вывод; его не следует вызывать напрямую.example — пример, который собирается обработать. got — фактический вывод примера. test — тест, содержащий example. out — функция вывода, переданная в
DocTestRunner.run().
-
report_failure(out, test, example, got) -
Сообщает, что данный пример завершился ошибкой. Этот метод предоставляется для того, чтобы подклассы
DocTestRunnerмогли настроить свой вывод; его не следует вызывать напрямую.example — пример, который собирается обработать. got — фактический вывод примера. test — тест, содержащий example. out — функция вывода, переданная в
DocTestRunner.run().
-
report_unexpected_exception(out, test, example, exc_info) -
Сообщает, что данный пример вызвал непредвиденное исключение. Этот метод предоставляется для того, чтобы подклассы
DocTestRunnerмогли настроить свой вывод; его не следует вызывать напрямую.example — пример, который собирается обработать. exc_info — кортеж, содержащий информацию об неожиданном исключении (как возвращается
sys.exc_info()). test — тест, содержащий example. out — функция вывода, переданная вDocTestRunner.run().
-
run(test, compileflags=None, out=None, clear_globs=True) -
Запустить примеры в test (объект
DocTest) и отобразить результаты с помощью функции записи out.Примеры выполняются в пространстве имен
test.globs. Если clear_globs равно True (по умолчанию), это пространство имен будет очищено после выполнения теста, чтобы помочь с сборкой мусора. Если вам нужно проверить пространство имен после завершения теста, используйте clear_globs=False.compileflags задает набор флагов, которые должны использоваться компилятором Python при выполнении примеров. Если не указано, по умолчанию используется набор флагов будущих импортов, применимых к globs.
Вывод каждого примера проверяется с помощью проверяющего вывода
DocTestRunner, а результаты форматируются методамиDocTestRunner.report_*().
-
summarize(verbose=None) -
Вывести сводку всех тестов, которые были выполнены этим DocTestRunner, и вернуть кортеж
TestResults(failed, attempted).Необязательный аргумент verbose управляет подробностью сводки. Если подробность не указана, используется подробность
DocTestRunner.
-
Объекты OutputChecker
-
class doctest.OutputChecker -
Класс, используемый для проверки соответствия фактического вывода из примера doctest ожидаемому выводу.
OutputCheckerопределяет два метода:check_output(), который сравнивает заданную пару выводов и возвращаетTrueесли они совпадают; иoutput_difference(), который возвращает строку, описывающую различия между двумя выводами.OutputCheckerопределяет следующие методы:-
check_output(want, got, optionflags) -
Возвращает
Trueесли фактический вывод из примера (got) совпадает с ожидаемым выводом (want). Эти строки всегда считаются совпадающими, если они идентичны; но в зависимости от флагов опций, используемых тестовым исполнителем, также возможны несколько типов неточного совпадения. См. раздел Флаги опций для получения дополнительной информации о флагах опций.
-
output_difference(example, got, optionflags) -
Возвращает строку, описывающую различия между ожидаемым выводом для данного примера (example) и фактическим выводом (got). optionflags — набор флагов опций, используемых для сравнения want и got.
-
Отладка
Модуль `doctest` предоставляет несколько механизмов для отладки примеров `doctest`:
- Несколько функций преобразуют `doctest` в исполняемые программы Python, которые можно запустить под отладчиком Python,
pdb. - Класс
DebugRunnerявляется подклассомDocTestRunnerи генерирует исключение для первого неудачного примера, содержащего информацию об этом примере. Эта информация может быть использована для постобъектной отладки примера. - Тесты
unittest, сгенерированныеDocTestSuite(), поддерживают методdebug(), определённый вunittest.TestCase. -
Вы можете добавить вызов
pdb.set_trace()в пример `doctest`, и при выполнении этой строки вы перейдёте в отладчик Python. Затем вы сможете проверить текущие значения переменных и т. д. Например, предположим, чтоa.pyсодержит только эту строку документации модуля:""" >>> def f(x): ... g(x*2) >>> def g(x): ... print(x+3) ... import pdb; pdb.set_trace() >>> f(3) 9 """
Тогда интерактивная сессия Python может выглядеть так:
>>> import a, doctest >>> doctest.testmod(a) --Return-- > <doctest a[1]>(3)g()->None -> import pdb; pdb.set_trace() (Pdb) list 1 def g(x): 2 print(x+3) 3 -> import pdb; pdb.set_trace() [EOF] (Pdb) p x 6 (Pdb) step --Return-- > <doctest a[0]>(2)f()->None -> g(x*2) (Pdb) list 1 def f(x): 2 -> g(x*2) [EOF] (Pdb) p x 3 (Pdb) step --Return-- > <doctest a[2]>(1)?()->None -> f(3) (Pdb) cont (0, 3) >>>
Функции, которые преобразуют тесты `doctest` в код Python и, возможно, запускают сгенерированный код в отладчике:
-
doctest.script_from_examples(s) -
Преобразование текста с примерами в сценарий.
Аргумент s — это строка, содержащая примеры `doctest`. Строка преобразуется в скрипт Python, где примеры `doctest` в s преобразуются в обычный код, а всё остальное преобразуется в комментарии Python. Сгенерированный скрипт возвращается в виде строки. Например,
import doctest print(doctest.script_from_examples(r""" Set x and y to 1 and 2. >>> x, y = 1, 2 Print their sum: >>> print(x+y) 3 """))отображает:
# Set x and y to 1 and 2. x, y = 1, 2 # # Print their sum: print(x+y) # Expected: ## 3
Эта функция используется во внутренних функциях (см. ниже), но также может быть полезна, когда вы хотите преобразовать интерактивную сессию Python в скрипт Python.
-
doctest.testsource(module, name) -
Преобразование примера `doctest` для объекта в скрипт.
Аргумент module — это объект модуля или имя модуля с точками, содержащий объект, чьи тесты `doctest` представляют интерес. Аргумент name — это имя (внутри модуля) объекта с тестами `doctest`, которые представляют интерес. Результат — строка, содержащая строку документации объекта, преобразованную в скрипт Python, как описано для
script_from_examples()выше. Например, если модульa.pyсодержит функцию верхнего уровняf(), тоimport a, doctest print(doctest.testsource(a, "a.f"))
выводит скриптовый вариант строки документации функции
f()с преобразованием тестов `doctest` в код, а остальное помещается в комментарии.
-
doctest.debug(module, name, pm=False) -
Отладка тестов `doctest` для объекта.
Аргументы module и name такие же, как и для функции
testsource()выше. Сгенерированный скрипт Python для строки документации указанного объекта записывается в временный файл, а затем этот файл выполняется под управлением отладчика Python,pdb.Для локального и глобального контекстов выполнения используется поверхностная копия
module.__dict__.Необязательный аргумент pm управляет использованием постобъектной отладки. Если pm имеет истинное значение, файл скрипта выполняется напрямую, и отладчик подключается только в том случае, если скрипт завершается путём повышения необработанного исключения. Если это произойдёт, то будет вызвана постобъектная отладка с помощью
pdb.post_mortem(), передавая объект стека вызовов необработанного исключения. Если pm не указан или является ложным, скрипт выполняется в отладчике с самого начала путём передачи соответствующего вызоваexec()вpdb.run().
-
doctest.debug_src(src, pm=False, globs=None) -
Отладка тестов `doctest` в строке.
Это аналогично функции
debug()выше, за исключением того, что строка, содержащая примеры `doctest`, указывается непосредственно через аргумент src.Необязательный аргумент pm имеет то же значение, что и в функции
debug()выше.Необязательный аргумент globs предоставляет словарь, используемый как для локального, так и для глобального контекста выполнения. Если не указан или
None, используется пустой словарь. Если указан, используется поверхностная копия словаря.
Класс DebugRunner и специфические исключения, которые он может генерировать, представляют наибольший интерес для авторов фреймворков тестирования и будут здесь лишь очерчены. Подробнее см. исходный код, а также строку документации DebugRunner (которая является примером `doctest`!):
-
class doctest.DebugRunner(checker=None, verbose=None, optionflags=0) -
Подкласс
DocTestRunner, который генерирует исключение, как только встречается неудача. Если происходит непредвиденное исключение, возникает исключениеUnexpectedException, содержащее тест, пример и исходное исключение. Если выходные данные не совпадают, то генерируется исключениеDocTestFailure, содержащее тест, пример и фактические выходные данные.Информация о параметрах конструктора и методах содержится в документации
DocTestRunnerв разделе Расширенный API.
Существует два исключения, которые могут быть возбуждены экземплярами DebugRunner:
-
exception doctest.DocTestFailure(test, example, got) -
Исключение, возбуждаемое
DocTestRunnerдля сигнализации о том, что фактические выходные данные примера `doctest` не соответствуют ожидаемым выходным данным. Аргументы конструктора используются для инициализации атрибутов с теми же именами.
В DocTestFailure определены следующие атрибуты:
-
DocTestFailure.test -
Объект
DocTest, который выполнялся во время неудачи примера.
-
DocTestFailure.example -
Пример
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().
Soapbox
Как упоминалось во введении, doctest получил три основных назначения:
- Проверка примеров в строках документации.
- Тестирование регрессии.
- Исполняемая документация/литературное тестирование.
Эти назначения требуют разного подхода, и важно их различать. В частности, заполнение строк документации запутанными тестовыми случаями плохо сказывается на документации.
При написании строки документации тщательно выбирайте примеры в этой строке. Это искусство, которое нужно освоить — вначале оно может казаться непривычным. Примеры должны приносить реальную пользу документации. Хороший пример часто стоит многих слов. Если подойти к этому с умом, примеры окажутся бесценными для ваших пользователей и окупят затраченное время на их сбор, по мере того как со временем будут происходить изменения. Я до сих пор поражаюсь, как часто один из моих doctest примеров перестаёт работать после «безобидного» изменения.
Doctest также является отличным инструментом для тестирования регрессии, особенно если вы не экономите на пояснительных текстах. Объединение прозы и примеров значительно облегчает отслеживание того, что фактически тестируется и почему. Когда тест терпит неудачу, хорошая проза существенно упрощает определение проблемы и способ её решения. Конечно, можно написать обширные комментарии в коде при тестировании, но немногие программисты это делают. Многие обнаружили, что использование подходов doctest приводит к гораздо более понятным тестам. Возможно, это просто потому, что doctest облегчает написание прозы по сравнению с написанием кода, в то время как написание комментариев в коде несколько сложнее. Я думаю, дело идёт глубже, чем просто это: естественное отношение при написании теста, основанного на doctest, заключается в стремлении объяснить тонкости вашего программного обеспечения и проиллюстрировать их примерами. Это, в свою очередь, естественным образом приводит к тестовым файлам, которые начинаются с простейших функций и логически переходят к сложностям и исключительным случаям. В результате получается связный рассказ, а не набор изолированных функций, которые тестируют изолированные фрагменты функциональности, по-видимому, случайным образом. Это другой подход, который даёт разные результаты, размывая грань между тестированием и объяснением.
Тестирование регрессии лучше всего ограничить специализированными объектами или файлами. Существует несколько вариантов организации тестов:
- Напишите текстовые файлы, содержащие тестовые примеры как интерактивные примеры, и протестируйте файлы с помощью
testfile()илиDocFileSuite(). Это рекомендуется, хотя это проще всего сделать для новых проектов, с самого начала спроектированных для использования doctest. - Определите функции с именем
_regrtest_topic, состоящие из одной строки документации, содержащей тестовые примеры для указанных тем. Эти функции можно включить в тот же файл, что и модуль, или вынести в отдельный тестовый файл. - Определите словарь
__test__, сопоставляющий темы регрессионного тестирования со строками документации, содержащими тестовые примеры.
Когда вы разместили свои тесты в модуле, сам модуль может выступать в качестве тестового исполнителя. При провале теста вы можете настроить своего тестового исполнителя на повторный запуск только не пройденного doctest во время отладки проблемы. Ниже приведён минимальный пример такого тестового исполнителя:
if __name__ == '__main__':
import doctest
flags = doctest.REPORT_NDIFF|doctest.FAIL_FAST
if len(sys.argv) > 1:
name = sys.argv[1]
if name in globals():
obj = globals()[name]
else:
obj = __test__[name]
doctest.run_docstring_examples(obj, globals(), name=name,
optionflags=flags)
else:
fail, total = doctest.testmod(optionflags=flags)
print("{} failures out of {} tests".format(fail, total))
Примечания
-
1 -
Примеры, содержащие как ожидаемый результат, так и исключение, не поддерживаются. Попытка угадать, где заканчивается один и начинается другой, слишком подвержена ошибкам, и это также делает тест запутанным.
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/doctest.html