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 есть много примеров доктестов. Особенно полезные примеры можно найти в стандартном тестовом файле Lib/test/test_doctest.py
.
Простое использование: Проверка примеров в документах¶
Самый простой способ начать использовать doctest (но не обязательно тот, которым вы будете продолжать это делать) - заканчивать каждый модуль M
символом:
if __name__ == "__main__":
import doctest
doctest.testmod()
doctest
затем рассматривает докстринги в модуле M
.
Запуск модуля как сценария приводит к тому, что примеры в документации выполняются и проверяются:
python M.py
Это не выведет ничего, пока какой-либо пример не потерпит неудачу, в этом случае неудачный пример(ы) и причина(ы) неудачи выводится в stdout, а последней строкой вывода будет ***Test Failed*** N failures.
, где N - количество примеров, которые потерпели неудачу.
Запустите его с переключателем -v
вместо этого:
python M.py -v
и подробный отчет обо всех испробованных примерах будет выведен на стандартный вывод, вместе с различными резюме в конце.
Вы можете включить режим verbose, передав 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
. Содержимое файла рассматривается так, как если бы это была одна гигантская doc-строка; файл не обязательно должен содержать программу на Python! Например, возможно, example.txt
содержит следующее:
The ``example`` module
======================
Using ``factorial``
-------------------
This is an example text file in reStructuredText format. First import
``factorial`` from the ``example`` module:
>>> from example import factorial
Now use it:
>>> factorial(6)
120
Выполнив doctest.testfile("example.txt")
, вы обнаружите ошибку в этой документации:
File "./example.txt", line 14, in example.txt
Failed example:
factorial(6)
Expected:
120
Got:
720
Как и testmod()
, testfile()
ничего не отображает, если пример не сработал. Если пример не удался, то неудачный пример(ы) и причина(и) неудачи выводятся в stdout, используя тот же формат, что и testmod()
.
По умолчанию testfile()
ищет файлы в каталоге вызывающего модуля. Описание дополнительных аргументов, которые можно использовать, чтобы указать модулю искать файлы в других местах, см. в разделе Базовый API.
Как и testmod()
, многословность testfile()
может быть установлена с помощью ключа командной строки -v
или с помощью необязательного ключевого аргумента verbose.
Существует также ярлык командной строки для запуска testfile()
. Вы можете указать интерпретатору Python запустить модуль doctest непосредственно из стандартной библиотеки и передать имя (имена) файла в командной строке:
python -m doctest -v example.txt
Поскольку имя файла не заканчивается на .py
, doctest
делает вывод, что его нужно запускать с помощью testfile()
, а не testmod()
.
Более подробную информацию о testfile()
см. в разделе Базовый API.
Как это работает¶
В этом разделе подробно рассматривается, как работает doctest: какие документы он просматривает, как он находит интерактивные примеры, какой контекст выполнения он использует, как он обрабатывает исключения, и как флаги опций могут быть использованы для управления его поведением. Это информация, которую вам нужно знать для написания примеров doctest; о том, как запустить doctest на этих примерах, читайте в следующих разделах.
Какие строки документов рассматриваются?¶
Выполняется поиск в строке документа модуля, а также во всех строках документов функций, классов и методов. Объекты, импортированные в модуль, не ищутся.
Кроме того, если M.__test__
существует и «является истиной», он должен быть диктой, и каждая запись сопоставляет (строковое) имя с объектом функции, объектом класса или строкой. Докстринги функций и объектов классов, найденные из M.__test__
, перебираются, а строки обрабатываются так, как если бы они были докстрингами. При выводе ключ K
в M.__test__
появляется с именем
<name of M>.__test__.K
Любые найденные классы рекурсивно перебираются аналогичным образом, чтобы проверить докстринги в содержащихся в них методах и вложенных классах.
Как распознаются примеры Docstring?¶
В большинстве случаев копирование и вставка интерактивной консольной сессии работает хорошо, но 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-колоночные упоры табуляции. Табуляции в выводе, генерируемом тестируемым кодом, не изменяются. Поскольку все символы табуляции в выводе примера расширяются, это означает, что если вывод кода включает символы табуляции, тест может пройти только в том случае, если действует опция
NORMALIZE_WHITESPACE
или directive. В качестве альтернативы, тест можно переписать так, чтобы перехватить вывод и сравнить его с ожидаемым значением как часть теста. Такой способ работы с табуляциями в исходном тексте был найден методом проб и ошибок и оказался наименее подверженным ошибкам. Можно использовать другой алгоритм обработки табуляции, написав собственный классDocTestParser
.Перехватывается вывод на stdout, но не вывод на stderr (трассировка исключений перехватывается другим способом).
Если вы продолжаете строку с помощью обратной косой черты в интерактивном сеансе или по какой-либо другой причине используете обратную косую черту, вам следует использовать необработанный docstring, который сохранит ваши обратные косые черты именно в том виде, в котором вы их набрали:
>>> 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
, и имена, определенные ранее в выполняемой doc-строке. Примеры не могут видеть имена, определенные в других документах.
Вы можете принудительно использовать свой собственный dict в качестве контекста выполнения, передавая 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
Этот тест проходит успешно, если поднимается ValueError
, с деталью list.remove(x): x not in list
, как показано на рисунке.
Ожидаемый вывод для исключения должен начинаться с заголовка traceback, который может быть любой из следующих двух строк с таким же отступом, как и первая строка примера:
Traceback (most recent call last):
Traceback (innermost last):
За заголовком traceback следует необязательный стек traceback, содержимое которого игнорируется 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
или просто выведен текст трассировки. На практике обычный вывод редко начинается со строки заголовка traceback, поэтому это не создает реальных проблем.Каждая строка стека обратного следа (если она присутствует) должна быть отступлена дальше, чем первая строка примера, или начинаться с неалфавитно-цифрового символа. Первая строка, следующая за заголовком трассировки с таким же отступом и начинающаяся с буквенно-цифрового символа, принимается за начало детализации исключения. Конечно, это правильно для настоящих возвратов.
Когда указана опция
IGNORE_EXCEPTION_DETAIL
doctest, все, что следует за крайним левым двоеточием, и любая информация о модуле в имени исключения игнорируется.Интерактивная оболочка опускает строку заголовка traceback для некоторых
SyntaxError
s. Но doctest использует строку заголовка traceback, чтобы отличить исключения от неисключений. Поэтому в редких случаях, когда вам нужно протестироватьSyntaxError
, в котором опущен заголовок traceback, вам придется вручную добавить строку заголовка traceback в ваш тестовый пример.
Для некоторых
SyntaxError
s, Python отображает позицию символа синтаксической ошибки, используя маркер^
:>>> 1 1 File "<stdin>", line 1 1 1 ^ SyntaxError: invalid syntax
Поскольку строки, показывающие местоположение ошибки, идут перед типом и деталью исключения, они не проверяются doctest. Например, следующий тест пройдет, даже если он поместит маркер
^
в неправильное место:>>> 1 1 File "<stdin>", line 1 1 1 ^ SyntaxError: invalid syntax
Флаги опций¶
Ряд опциональных флагов управляет различными аспектами поведения doctest. Символические имена флагов предоставляются в виде констант модуля, которые можно bitwise ORed собрать вместе и передать различным функциям. Имена также могут быть использованы в doctest directives и могут быть переданы интерфейсу командной строки doctest через опцию -o
.
Добавлено в версии 3.4: Опция командной строки -o
.
Первая группа опций определяет семантику теста, контролируя аспекты того, как doctest решает, соответствует ли фактический результат ожидаемому результату примера:
-
doctest.
DONT_ACCEPT_TRUE_FOR_1
¶ По умолчанию, если ожидаемый блок вывода содержит только
1
, то фактический блок вывода, содержащий только1
или толькоTrue
, считается совпадением, и аналогично для0
противFalse
. Когда указаноDONT_ACCEPT_TRUE_FOR_1
, ни одна из подстановок не допускается. Поведение по умолчанию учитывает то, что Python изменил возвращаемый тип многих функций с целого числа на boolean; тесты, ожидающие вывода «маленького целого», все еще работают в этих случаях. Возможно, эта опция исчезнет, но только через несколько лет.
-
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
. Он также проигнорирует любое полное имя, включенное перед классом исключения, которое может отличаться в разных реализациях и версиях Python и используемого кода/библиотек. Таким образом, все три варианта будут работать с указанным флагом:>>> raise Exception('message') Traceback (most recent call last): Exception: message >>> raise Exception('message') Traceback (most recent call last): builtins.Exception: message >>> raise Exception('message') Traceback (most recent call last): __main__.Exception: message
Обратите внимание, что
ELLIPSIS
также можно использовать для игнорирования деталей сообщения об исключении, но такая проверка все равно может завершиться неудачей на основании наличия или точного совпадения имени модуля.Изменено в версии 3.2:
IGNORE_EXCEPTION_DETAIL
теперь также игнорирует любую информацию, относящуюся к модулю, содержащему тестируемое исключение.
-
doctest.
SKIP
¶ Если указано, не запускать пример вообще. Это может быть полезно в ситуациях, когда примеры doctest служат одновременно документацией и тестовыми примерами, и пример должен быть включен для целей документации, но не должен проверяться. Например, вывод примера может быть случайным; или пример может зависеть от ресурсов, которые недоступны для водителя теста.
Флаг SKIP также можно использовать для временного «комментирования» примеров.
-
doctest.
COMPARISON_FLAGS
¶ Битовая маска или объединение всех вышеперечисленных флагов сравнения.
Вторая группа опций управляет тем, как сообщается о сбоях в тестировании:
-
doctest.
REPORT_UDIFF
¶ Если указано, сбои, включающие многострочные ожидаемые и фактические результаты, отображаются с помощью унифицированного diff.
-
doctest.
REPORT_CDIFF
¶ Если указано, сбои, включающие многострочные ожидаемые и фактические результаты, будут отображаться с помощью контекстного diff.
-
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 могут быть использованы для изменения option flags для отдельного примера. Директивы 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))) # doctest: +NORMALIZE_WHITESPACE
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Без директивы он не пройдет, как потому, что фактический вывод не имеет двух пробелов перед элементами одноразрядного списка, так и потому, что фактический вывод находится в одной строке. Этот тест также проходит, и для этого также требуется директива:
>>> print(list(range(20))) # doctest: +ELLIPSIS
[0, 1, ..., 18, 19]
На одной физической строке можно использовать несколько директив, разделяя их запятыми:
>>> print(list(range(20))) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]
Если для одного примера используется несколько директивных комментариев, то они объединяются:
>>> print(list(range(20))) # doctest: +ELLIPSIS
... # doctest: +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]
Как показано в предыдущем примере, вы можете добавить в пример строки ...
, содержащие только директивы. Это может быть полезно, когда пример слишком длинный, чтобы директивы могли удобно разместиться на одной строке:
>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40)))
... # doctest: +ELLIPSIS
[0, ..., 4, 10, ..., 19, 30, ..., 39]
Обратите внимание, что поскольку по умолчанию все опции отключены, а директивы применяются только к примеру, в котором они появляются, включение опций (через +
в директиве) обычно является единственным значимым выбором. Однако флаги опций могут также передаваться функциям, запускающим доктесты, устанавливая различные значения по умолчанию. В таких случаях отключение опции через -
в директиве может быть полезным.
Предупреждения¶
doctest
серьезно относится к требованию точного совпадения в ожидаемом выводе. Если хотя бы один символ не совпадает, тест проваливается. Возможно, это несколько раз удивит вас, когда вы узнаете, что именно Python гарантирует и чего не гарантирует в выводе. Например, при печати множества Python не гарантирует, что элементы будут выведены в определенном порядке, поэтому тест типа
>>> foo()
{"Hermione", "Harry"}
уязвима! Одно из обходных решений - сделать
>>> foo() == {"Hermione", "Harry"}
True
вместо. Другой вариант - сделать
>>> d = sorted(foo())
>>> d
['Harry', 'Hermione']
Есть и другие, но вы поняли идею.
Еще одна плохая идея - печатать вещи, которые содержат адрес объекта, например
>>> id(1.0) # certain to fail some of the time
7948648
>>> class C: pass
>>> C() # the default repr() for instances embeds an address
<C object at 0x00AC18F0>
Директива ELLIPSIS
дает хороший подход для последнего примера:
>>> C() # doctest: +ELLIPSIS
<C object 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
безопасны на всех платформах, и я часто придумываю доктесты, чтобы получить числа такого вида:
>>> 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 создается новая неглубокая копия этого dict, так что его примеры начинаются с чистого листа. По умолчанию, или если
None
, используется новый пустой dict.Необязательный аргумент extraglobs дает дикту, объединенную с globals, используемыми для выполнения примеров. Это работает как
dict.update()
: если globs и extraglobs имеют общий ключ, то соответствующее значение в extraglobs появляется в объединенном dict. По умолчанию, или еслиNone
, дополнительные глобалы не используются. Это расширенная возможность, которая позволяет параметризовать доктесты. Например, доктест может быть написан для базового класса, используя общее имя класса, а затем повторно использоваться для тестирования любого количества подклассов путем передачи диктанта extraglobs, отображающего общее имя на тестируемый подкласс.Необязательный аргумент verbose печатает много информации, если true, и печатает только ошибки, если false; по умолчанию, или если
None
, это true тогда и только тогда, когда'-v'
находится вsys.argv
.Необязательный аргумент report печатает сводку в конце, если true, иначе в конце ничего не печатается. В режиме verbose сводка подробная, в противном случае сводка очень краткая (фактически пустая, если все тесты пройдены).
Необязательный аргумент optionflags (значение по умолчанию 0) принимает bitwise OR флагов опций. См. раздел Флаги опций.
Необязательный аргумент raise_on_error по умолчанию имеет значение false. Если значение равно true, исключение будет поднято при первом сбое или неожиданном исключении в примере. Это позволяет отлаживать сбои после их завершения. Поведение по умолчанию - продолжать выполнение примеров.
Необязательный аргумент parser указывает
DocTestParser
(или подкласс), который должен использоваться для извлечения тестов из файлов. По умолчанию используется обычный парсер (т.е.DocTestParser()
).Необязательный аргумент encoding указывает кодировку, которая должна использоваться для преобразования файла в юникод.
-
doctest.
testmod
(m=None, name=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, exclude_empty=False)¶ Все аргументы являются необязательными, и все, кроме m, должны быть указаны в форме ключевого слова.
Тестовые примеры в docstrings в функциях и классах, достижимых из модуля 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.master.summarize()
в сочетании сtestmod()
, продолжает получать вывод для объектов без тестов. Аргумент exclude_empty в более новом конструктореDocTestFinder
по умолчанию равен true.Дополнительные аргументы extraglobs, verbose, report, optionflags, raise_on_error и globs такие же, как и для функции
testfile()
выше, за исключением того, что globs по умолчанию имеет значениеm.__dict__
.
-
doctest.
run_docstring_examples
(f, globs, verbose=False, name='NoName', compileflags=None, optionflags=0)¶ Тестовые примеры, связанные с объектом f; например, f может быть строкой, модулем, функцией или объектом класса.
Для контекста выполнения используется неглубокая копия аргумента словаря globs.
Необязательный аргумент name используется в сообщениях о сбоях и по умолчанию имеет значение
"NoName"
.Если необязательный аргумент verbose равен true, вывод генерируется даже при отсутствии сбоев. По умолчанию вывод осуществляется только в случае сбоя примера.
Необязательный аргумент compileflags задает набор флагов, которые должны использоваться компилятором Python при выполнении примеров. По умолчанию или если
None
, флаги выводятся в соответствии с набором будущих возможностей, найденных в globs.Необязательный аргумент optionflags работает как для функции
testfile()
выше.
Unittest API¶
По мере роста вашей коллекции doctest’s ed модулей, вам понадобится способ систематического запуска всех их доктестов. doctest
предоставляет две функции, которые можно использовать для создания unittest
тестовых наборов из модулей и текстовых файлов, содержащих доктесты. Для интеграции с unittest
обнаружения тестов, включите функцию load_tests()
в ваш тестовый модуль:
import unittest
import doctest
import my_module_with_doctests
def load_tests(loader, tests, ignore):
tests.addTests(doctest.DocTestSuite(my_module_with_doctests))
return tests
Есть две основные функции для создания экземпляров unittest.TestSuite
из текстовых файлов и модулей с доктестами:
-
doctest.
DocFileSuite
(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None)¶ Преобразование тестов 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 указывает кодировку, которая должна использоваться для преобразования файла в юникод.
Глобал
__file__
добавляется к глобалам, предоставляемым доктестам, загруженным из текстового файла с помощьюDocFileSuite()
.
-
doctest.
DocTestSuite
(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None)¶ Преобразование тестов doctest для модуля в
unittest.TestSuite
.Возвращаемый
unittest.TestSuite
запускается фреймворком unittest и выполняет каждый доктест в модуле. Если какой-либо из доктестов не работает, то синтезированный модульный тест не работает, и возникает исключениеfailureException
, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.Необязательный аргумент module указывает модуль, который будет тестироваться. Это может быть объект модуля или имя модуля (возможно, с точкой). Если он не указан, используется модуль, вызывающий эту функцию.
Необязательный аргумент globs - это словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию globs - это новый пустой словарь.
Необязательный аргумент extraglobs задает дополнительный набор глобальных переменных, который объединяется в globs. По умолчанию дополнительные глобальные переменные не используются.
Необязательный аргумент test_finder - это объект
DocTestFinder
(или его замена), который используется для извлечения доктестов из модуля.Дополнительные аргументы setUp, tearDown и optionflags такие же, как и для функции
DocFileSuite()
выше.Эта функция использует ту же технику поиска, что и
testmod()
.Изменено в версии 3.5:
DocTestSuite()
возвращает пустойunittest.TestSuite
, если module не содержит docstrings, вместо того, чтобы поднимать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 принимает bitwise OR флагов опций. См. раздел Флаги опций. Можно использовать только «флаги отчетности».
Это глобальная настройка модуля, которая влияет на все будущие тесты, выполняемые модулем
unittest
: методrunTest()
изDocTestCase
просматривает флаги опций, указанные для тестового случая при создании экземпляраDocTestCase
. Если флаги отчетов не были указаны (что является типичным и ожидаемым случаем), флаги отчетовdoctest
вunittest
переходят bitwise ORed в флаги опций, и дополненные таким образом флаги опций передаются экземпляруDocTestRunner
, созданному для выполнения теста. Если при создании экземпляраDocTestCase
были указаны какие-либо флаги отчетности, то флаги отчетностиdoctest
отunittest
игнорируются.Функция возвращает значение флагов отчетности
unittest
, действовавших до вызова функции.
Расширенный API¶
Базовый API - это простая обертка, предназначенная для простоты использования doctest. Он достаточно гибкий и должен удовлетворить потребности большинства пользователей; однако, если вам требуется более тонкий контроль над тестированием или вы хотите расширить возможности doctest, то вам следует использовать расширенный API.
Расширенный API вращается вокруг двух контейнерных классов, которые используются для хранения интерактивных примеров, извлеченных из кейсов doctest:
Example
: Одиночный Python statement, в паре с ожидаемым выводом.DocTest
: КоллекцияExample
s, обычно извлекаемая из одной строки документа или текстового файла.
Определены дополнительные классы обработки для поиска, разбора и запуска, а также проверки примеров doctest:
DocTestFinder
: Находит все docstrings в данном модуле и используетDocTestParser
для созданияDocTest
из каждой docstring, которая содержит интерактивные примеры.DocTestParser
: Создает объектDocTest
из строки (например, docstring объекта).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
¶ Пространство имен (оно же globals), в котором должны выполняться примеры. Это словарь, отображающий имена на значения. Любые изменения в пространстве имен, сделанные примерами (например, привязка новых переменных), будут отражены в
globs
после выполнения теста.
-
name
¶ Строковое имя, идентифицирующее
DocTest
. Обычно это имя объекта или файла, из которого был извлечен тест.
-
filename
¶ Имя файла, из которого был извлечен данный
DocTest
; илиNone
, если имя файла неизвестно, или еслиDocTest
не был извлечен из файла.
-
lineno
¶ Номер строки в
filename
, с которой начинается данныйDocTest
, илиNone
, если номер строки недоступен. Номер строки равен нулю по отношению к началу файла.
-
docstring
¶ Строка, из которой был извлечен тест, или
None
, если строка недоступна, или если тест не был извлечен из строки.
-
Примеры объектов¶
-
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
, который используется для переопределения опций по умолчанию для данного примера. Любые флаги опций, не содержащиеся в этом словаре, оставляются в значении по умолчанию (как указано вDocTestRunner
“optionflags
). По умолчанию никакие опции не установлены.
-
Объекты DocTestFinder¶
-
class
doctest.
DocTestFinder
(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)¶ Класс обработки, используемый для извлечения
DocTest
s, относящихся к данному объекту, из его doc-строки и doc-строк содержащихся в нем объектов.DocTest
s могут быть извлечены из модулей, классов, функций, методов, статических методов, методов класса и свойств.Необязательный аргумент verbose может использоваться для отображения объектов, которые ищет искатель. По умолчанию он имеет значение
False
(вывод отсутствует).Необязательный аргумент parser задает объект
DocTestParser
(или замену), который используется для извлечения доктестов из docstrings.Если дополнительный аргумент recurse равен false, то
DocTestFinder.find()
будет исследовать только данный объект, а не любые содержащиеся в нем объекты.Если необязательный аргумент exclude_empty равен false, то
DocTestFinder.find()
будет включать тесты для объектов с пустыми docstrings.DocTestFinder
определяет следующий метод:-
find
(obj[, name][, module][, globs][, extraglobs])¶ Возвращает список
DocTest
s, которые определены docstring’ом obj или docstring’ами любого из содержащихся в нем объектов.Необязательный аргумент name задает имя объекта; это имя будет использоваться для построения имен возвращаемых
DocTest
s. Если name не указано, то используетсяobj.__name__
.Необязательный параметр module - это модуль, который содержит данный объект. Если модуль не указан или равен
None
, то программа поиска попытается автоматически определить правильный модуль. Используется модуль объекта:В качестве пространства имен по умолчанию, если не указано globs.
Чтобы предотвратить извлечение DocTestFinder’ом DocTests из объектов, импортированных из других модулей. (Содержащиеся объекты с модулями, отличными от module, игнорируются).
Чтобы найти имя файла, содержащего объект.
Помогает найти номер строки объекта в его файле.
Если module имеет значение
False
, то попытка найти модуль не предпринимается. Это неясный момент, полезный в основном для тестирования самого doctest: если module равенFalse
, или равенNone
, но не может быть найден автоматически, то все объекты считаются принадлежащими (несуществующему) модулю, поэтому все содержащиеся в нем объекты будут (рекурсивно) искаться doctest.Словарь globals для каждого
DocTest
формируется путем объединения globs и extraglobs (привязки в extraglobs переопределяют привязки в globs). Для каждогоDocTest
создается новая неглубокая копия словаря globals. Если globs не указан, то по умолчанию используется __dict__ модуля, если он указан, или{}
в противном случае. Если extraglobs не указан, то по умолчанию используется значение{}
.
-
Объекты DocTestParser¶
-
class
doctest.
DocTestParser
¶ Класс обработки, используемый для извлечения интерактивных примеров из строки и их использования для создания объекта
DocTest
.DocTestParser
определяет следующие методы:-
get_doctest
(string, globs, name, filename, lineno)¶ Извлечь все примеры doctest из заданной строки и собрать их в объект
DocTest
.globs, name, filename и lineno являются атрибутами нового объекта
DocTest
. Более подробную информацию смотрите в документации дляDocTest
.
-
get_examples
(string, name='<string>')¶ Извлечь все примеры doctest из заданной строки и вернуть их в виде списка объектов
Example
. Номера строк основаны на 0. Необязательный аргумент name является именем, идентифицирующим данную строку, и используется только для сообщений об ошибках.
-
parse
(string, name='<string>')¶ Разделите заданную строку на примеры и промежуточный текст и верните их в виде списка чередующихся
Example
s и строк. Номера строк дляExample
s основаны на 0. Необязательный аргумент name является именем, идентифицирующим данную строку, и используется только для сообщений об ошибках.
-
Объекты DocTestRunner¶
-
class
doctest.
DocTestRunner
(checker=None, verbose=None, optionflags=0)¶ Класс обработки, используемый для выполнения и проверки интерактивных примеров в
DocTest
.Сравнение между ожидаемыми и фактическими выходами выполняется с помощью
OutputChecker
. Это сравнение может быть настроено с помощью ряда флагов опций; более подробную информацию см. в разделе Флаги опций. Если флагов опций недостаточно, то сравнение также можно настроить, передав конструктору подклассOutputChecker
.Выводом на экран бегуна тестирования можно управлять двумя способами. Во-первых, в
TestRunner.run()
можно передать функцию вывода; эта функция будет вызываться со строками, которые должны быть выведены на экран. По умолчанию она принимает значениеsys.stdout.write
. Если перехвата вывода недостаточно, то вывод на экран можно также настроить, подклассифицировав DocTestRunner и переопределив методыreport_start()
,report_success()
,report_unexpected_exception()
иreport_failure()
.Необязательный аргумент checker задает объект
OutputChecker
(или замену), который должен использоваться для сравнения ожидаемых результатов с фактическими результатами примеров doctest.Необязательный аргумент ключевого слова verbose управляет многословностью
DocTestRunner
. Если verbose равноTrue
, то информация печатается о каждом примере по мере его выполнения. Если verbose равноFalse
, то печатаются только ошибки. Если verbose не задано илиNone
, то при использовании переключателя командной строки-v
будет выводиться подробная информация.Необязательный аргумент optionflags можно использовать для управления тем, как программа запуска тестов сравнивает ожидаемый результат с фактическим, и как она отображает ошибки. Для получения дополнительной информации см. раздел Флаги опций.
DocTestParser
определяет следующие методы:-
report_start
(out, test, example)¶ Сообщить, что программа запуска тестов собирается обработать данный пример. Этот метод предоставляется для того, чтобы подклассы
DocTestRunner
могли настраивать свой вывод; его не следует вызывать напрямую.example - это пример, который будет обработан. test - это тест, содержащий пример. 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
) и выведите результаты с помощью функции writer out.Примеры выполняются в пространстве имен
test.globs
. Если clear_globs равно true (по умолчанию), то это пространство имен будет очищено после выполнения теста, чтобы помочь в сборке мусора. Если вы хотите просмотреть пространство имен после завершения теста, используйте clear_globs=False.compileflags задает набор флагов, которые должны использоваться компилятором Python при выполнении примеров. Если он не указан, то по умолчанию будет использоваться набор флагов будущего импорта, который применяется к globs.
Вывод каждого примера проверяется с помощью средства проверки вывода
DocTestRunner
, а результаты форматируются методамиDocTestRunner.report_*()
.
-
summarize
(verbose=None)¶ Выводит сводку всех тестовых случаев, которые были запущены этим DocTestRunner, и возвращает named tuple
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:
Несколько функций преобразуют доктесты в исполняемые программы Python, которые можно запускать под отладчиком Python,
pdb
.Класс
DebugRunner
является подклассом классаDocTestRunner
, который вызывает исключение для первого неудачного примера, содержащее информацию об этом примере. Эта информация может быть использована для посмертной отладки примера.Случаи
unittest
, генерируемыеDocTestSuite()
, поддерживают методdebug()
, определенныйunittest.TestCase
.Вы можете добавить вызов
pdb.set_trace()
в пример doctest, и вы попадете в отладчик Python, когда эта строка будет выполнена. Затем вы можете проверить текущие значения переменных и так далее. Например, предположим, чтоa.py
содержит только этот модуль docstring:""" >>> 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) >>>
Функции, которые преобразуют доктесты в код Python и, возможно, запускают синтезированный код под отладчиком:
-
doctest.
script_from_examples
(s)¶ Преобразуйте текст с примерами в сценарий.
Аргумент s - это строка, содержащая примеры доктестов. Строка преобразуется в сценарий 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)¶ Преобразование теста для объекта в сценарий.
Аргумент module - это объект модуля или точечное имя модуля, содержащего объект, чьи доктесты представляют интерес. Аргумент name - имя (внутри модуля) объекта с интересующими доктестами. Результатом будет строка, содержащая doc-строку объекта, преобразованную в сценарий Python, как описано выше для
script_from_examples()
. Например, если модульa.py
содержит функцию верхнего уровняf()
, тоimport a, doctest print(doctest.testsource(a, "a.f"))
печатает скриптовую версию docstring функции
f()
, при этом доктесты преобразуются в код, а остальное помещается в комментарии.
-
doctest.
debug
(module, name, pm=False)¶ Отладка доктестов для объекта.
Аргументы модуль и имя такие же, как и для функции
testsource()
выше. Синтезированный сценарий Python для docstring именованного объекта записывается во временный файл, а затем этот файл запускается под управлением отладчика Python,pdb
.Неглубокая копия
module.__dict__
используется как для локального, так и для глобального контекста выполнения.Необязательный аргумент pm определяет, используется ли посмертная отладка. Если pm имеет значение true, то файл сценария запускается напрямую, и отладчик подключается только в том случае, если сценарий завершается, вызвав необработанное исключение. Если это происходит, то вызывается посмертная отладка, через
pdb.post_mortem()
, передавая объект трассировки не обработанного исключения. Если pm не указан или равен false, то сценарий запускается под отладчиком с самого начала, передавая соответствующий вызовexec()
вpdb.run()
.
-
doctest.
debug_src
(src, pm=False, globs=None)¶ Отладка доктестов в строке.
Это похоже на функцию
debug()
выше, за исключением того, что строка, содержащая примеры doctest, указывается напрямую, через аргумент src.Необязательный аргумент pm имеет то же значение, что и в функции
debug()
выше.Необязательный аргумент globs задает словарь для использования в качестве локального и глобального контекста выполнения. Если не указан или
None
, используется пустой словарь. Если указан, то используется неглубокая копия словаря.
Класс DebugRunner
и специальные исключения, которые он может вызывать, представляют наибольший интерес для авторов тестовых фреймворков, и здесь мы ограничимся лишь кратким описанием. Более подробную информацию смотрите в исходном коде, и особенно в docstring 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.
got
¶ Фактический выход примера.
-
exception
doctest.
UnexpectedException
(test, example, exc_info)¶ Исключение, вызываемое
DocTestRunner
для сигнализации о том, что пример doctest вызвал неожиданное исключение. Аргументы конструктора используются для инициализации одноименных атрибутов.
UnexpectedException
определяет следующие атрибуты:
-
UnexpectedException.
exc_info
¶ Кортеж, содержащий информацию о неожиданном исключении, возвращенную командой
sys.exc_info()
.
Мыльница¶
Как уже упоминалось во введении, doctest
имеет три основных применения:
Проверка примеров в документах.
Регрессионное тестирование.
Исполняемая документация / грамотное тестирование.
Эти виды использования имеют разные требования, и важно их различать. В частности, заполнение документации непонятными тестовыми случаями делает документацию плохой.
При написании docstring выбирайте примеры docstring с осторожностью. В этом есть определенное искусство, которому нужно научиться - сначала это может быть неестественно. Примеры должны добавлять реальную ценность документации. Хороший пример часто стоит многих слов. Если примеры сделаны тщательно, они будут бесценны для ваших пользователей и многократно окупят время, потраченное на их сбор, по мере того, как годы будут идти и ситуация будет меняться. Я все еще поражаюсь тому, как часто один из моих примеров doctest
перестает работать после «безобидного» изменения.
Doctest также является отличным инструментом для регрессионного тестирования, особенно если вы не скупитесь на пояснительный текст. Чередуя прозу и примеры, становится намного проще отслеживать, что именно тестируется и почему. Когда тест не работает, хорошая проза может значительно облегчить выяснение того, в чем проблема и как ее устранить. Это правда, что при тестировании на основе кода можно писать обширные комментарии, но немногие программисты так делают. Многие обнаружили, что использование подходов doctest вместо этого приводит к гораздо более четким тестам. Возможно, это просто потому, что doctest делает написание прозы немного проще, чем написание кода, в то время как написание комментариев в коде немного сложнее. Я думаю, что дело не только в этом: естественное отношение при написании теста на основе doctest заключается в том, что вы хотите объяснить тонкости вашего программного обеспечения и проиллюстрировать их примерами. Это, в свою очередь, естественно приводит к тому, что тестовые файлы начинаются с самых простых функций и логически переходят к сложностям и крайним случаям. В результате получается связное повествование, а не набор изолированных функций, которые тестируют отдельные фрагменты функциональности как бы наугад. Это другое отношение, и оно дает другие результаты, стирая различие между тестированием и объяснением.
Регрессионное тестирование лучше всего ограничить специальными объектами или файлами. Существует несколько вариантов организации тестов:
Напишите текстовые файлы, содержащие тестовые случаи в качестве интерактивных примеров, и протестируйте файлы, используя
testfile()
илиDocFileSuite()
. Это рекомендуется, хотя проще всего это сделать для новых проектов, с самого начала рассчитанных на использование doctest.Определите функции с именем
_regrtest_topic
, которые состоят из отдельных докстрок, содержащих тестовые случаи для названных тем. Эти функции могут быть включены в тот же файл, что и модуль, или выделены в отдельный тестовый файл.Определите отображение словаря
__test__
от тем регрессионных тестов к документам, содержащим тестовые случаи.
Если вы поместили свои тесты в модуль, модуль сам может быть программой запуска тестов. Если тест не работает, вы можете организовать запуск теста так, чтобы он повторно выполнял только неработающий тест, пока вы отлаживаете проблему. Вот минимальный пример такой программы запуска тестов:
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
Примеры, содержащие как ожидаемый вывод, так и исключение, не поддерживаются. Попытка угадать, где заканчивается одно и начинается другое, слишком чревата ошибками, и это также делает тест запутанным.