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 есть много примеров doctests. Особенно полезные примеры можно найти в стандартном файле тестов 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
Любые найденные классы аналогично проверяются рекурсивно для проверки строк документации в содержащихся в них методах и вложенных классах.
Как распознаются примеры в строках документации?
В большинстве случаев копирование и вставка сеанса интерактивной консоли работает хорошо, но 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, всё, что следует за левым двоеточием и любой информацией о модуле в имени исключения, игнорируется. - Интерактивная оболочка пропускает строку заголовка трассировки для некоторых
SyntaxError. Но doctest использует строку заголовка трассировки для различения исключений и не-исключений. Таким образом, в редком случае, когда вам нужно проверитьSyntaxError, который пропускает строку заголовка трассировки, вам нужно вручную добавить строку заголовка трассировки к вашему тестовому примеру.
-
Для некоторых
SyntaxError, Python отображает позицию символа синтаксической ошибки, используя маркер^:>>> 1 1 File "<stdin>", line 1 1 1 ^ SyntaxError: invalid syntaxПоскольку строки, показывающие позицию ошибки, появляются перед типом и подробностями исключения, doctest их не проверяет. Например, следующий тест прошёл бы, даже если он поместил маркер
^в неправильное место:>>> 1 1 File "<stdin>", line 1 1 1 ^ SyntaxError: invalid syntax
Флаги опций
Несколько флагов опций управляют различными аспектами поведения 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 -
При задании doctest, ожидающий исключения, проходит, если возникает исключение ожидаемого типа, даже если детали (сообщение и полное имя исключения) не совпадают.
Например, пример, ожидающий
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 -
При задании сбои, связанные с многострочным ожидаемым и фактическим выводом, отображаются с помощью унифицированного различия.
-
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))) # 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, устанавливая различные значения по умолчанию. В таких случаях отключение опции с помощью - в директиве может быть полезным.
Предупреждения
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, а качество таких библиотек сильно различается.
>>> 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, иначе ничего не выводит в конце. В режиме подробной информации сводка содержит подробности, иначе сводка очень краткая (на самом деле пустая, если все тесты прошли).
Необязательный аргумент optionflags (значение по умолчанию 0) принимает побитовое ИЛИ флагов опций. См. раздел Побитовая операция ИЛИ.
Необязательный аргумент raise_on_error по умолчанию false. Если true, при первой ошибке или непредвиденном исключении в примере генерируется исключение. Это позволяет отлаживать ошибки постфактум. По умолчанию выполнение примеров продолжается.
Необязательный аргумент 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: КоллекцияExamples, обычно извлекаемых из одной строки документации или текстового файла.
Для поиска, анализа, выполнения и проверки примеров 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, который используется для переопределения стандартных опций для этого примера. Все флаги опций, не включённые в этот словарь, остаются по умолчанию (как указано вDocTestRunner’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:
- Несколько функций преобразуют doctests в исполняемые программы Python, которые можно запустить под отладчиком Python,
pdb. - Класс
DebugRunnerявляется подклассомDocTestRunner, который вызывает исключение для первого не пройденного примера, содержащего информацию об этом примере. Эта информация может быть использована для проведения постобработки отладки примера. - Примеры
unittest, сгенерированныеDocTestSuite(), поддерживают методdebug(), определённый вunittest.TestCase. -
Вы можете добавить вызов
pdb.set_trace()в пример doctest, и вы попадете в отладчик Python, когда будет выполнена эта строка. Затем вы можете проверить текущие значения переменных и так далее. Например, предположим, чтоa.pyсодержит только эту строку документации модуля:""" >>> def f(x): ... g(x*2) >>> def g(x): ... print(x+3) ... import pdb; pdb.set_trace() >>> f(3) 9 """
Тогда интерактивная сессия Python может выглядеть так:
>>> import a, doctest >>> doctest.testmod(a) --Return-- > <doctest a[1]>(3)g()->None -> import pdb; pdb.set_trace() (Pdb) list 1 def g(x): 2 print(x+3) 3 -> import pdb; pdb.set_trace() [EOF] (Pdb) p x 6 (Pdb) step --Return-- > <doctest a[0]>(2)f()->None -> g(x*2) (Pdb) list 1 def f(x): 2 -> g(x*2) [EOF] (Pdb) p x 3 (Pdb) step --Return-- > <doctest a[2]>(1)?()->None -> f(3) (Pdb) cont (0, 3) >>>
Функции, которые преобразуют doctests в код Python, и, возможно, выполняют сгенерированный код в отладчике:
-
doctest.script_from_examples(s) -
Преобразовать текст с примерами в сценарий.
Аргумент s — это строка, содержащая примеры doctest. Строка преобразуется в скрипт Python, где примеры doctest в s преобразуются в обычный код, а все остальное — в комментарии Python. Сгенерированный скрипт возвращается в виде строки. Например,
import doctest print(doctest.script_from_examples(r""" Set x and y to 1 and 2. >>> x, y = 1, 2 Print their sum: >>> print(x+y) 3 """))отображает:
# Set x and y to 1 and 2. x, y = 1, 2 # # Print their sum: print(x+y) # Expected: ## 3
Эта функция используется внутри другими функциями (см. ниже), но также может быть полезна, когда вы хотите преобразовать интерактивную сессию Python в скрипт Python.
-
doctest.testsource(module, name) -
Преобразовать doctest для объекта в скрипт.
Аргумент module — это объект модуля или имя модуля, содержащий объект, doctests которого представляют интерес. Аргумент name — имя (внутри модуля) объекта с интересующими doctests. Результат — строка, содержащая строку документации объекта, преобразованную в скрипт Python, как описано для
script_from_examples()выше. Например, если модульa.pyсодержит функцию верхнего уровняf(), тоimport a, doctest print(doctest.testsource(a, "a.f"))
выводит версию скрипта строки документации функции
f(), с doctests, преобразованными в код, а остальное — в комментарии.
-
doctest.debug(module, name, pm=False) -
Отладить doctests для объекта.
Аргументы module и name такие же, как для функции
testsource()выше. Сгенерированный скрипт Python для строки документации указанного объекта записывается во временный файл, а затем этот файл выполняется под управлением отладчика Python,pdb.Для локального и глобального контекста выполнения используется неглубокая копия
module.__dict__.Необязательный аргумент pm управляет использованием постобработки отладки. Если pm имеет истинное значение, файл скрипта выполняется непосредственно, а отладчик вмешивается только в том случае, если скрипт завершается с помощью повышения необработанного исключения. Если это произойдет, то вызовется постобработка отладки, с помощью
pdb.post_mortem(), передавая объект трассировки из необработанного исключения. Если pm не указан или ложно, скрипт выполняется в отладчике с самого начала, передавая соответствующий вызовexec()вpdb.run().
-
doctest.debug_src(src, pm=False, globs=None) -
Отладить doctests в строке.
Это аналогично функции
debug()выше, за исключением того, что строка, содержащая примеры doctest, задается непосредственно через аргумент src.Необязательный аргумент pm имеет то же значение, что и в функции
debug()выше.Необязательный аргумент globs задаёт словарь для использования в качестве локального и глобального контекста выполнения. Если не указан или
None, используется пустой словарь. Если указан, используется неглубокая копия словаря.
Класс DebugRunner и специальные исключения, которые он может вызывать, представляют наибольший интерес для авторов фреймворков тестирования и будут здесь лишь набросаны. Более подробные сведения см. в исходном коде, и особенно в строке документации DebugRunner (которая представляет собой doctest!):
-
class doctest.DebugRunner(checker=None, verbose=None, optionflags=0) -
Подкласс
DocTestRunner, который вызывает исключение, как только возникает ошибка. Если возникает непредвиденное исключение, возникает исключениеUnexpectedException, содержащее тест, пример и исходное исключение. Если вывод не совпадает, то возникает исключениеDocTestFailure, содержащее тест, пример и фактический вывод.Сведения о параметрах конструктора и методах см. в документации для
DocTestRunnerв разделе Расширенный API.
Существует два исключения, которые могут быть вызваны экземплярами DebugRunner:
-
exception doctest.DocTestFailure(test, example, got) -
Исключение, вызываемое
DocTestRunner, чтобы сигнализировать о том, что фактический вывод примера doctest не совпал с ожидаемым выводом. Аргументы конструктора используются для инициализации атрибутов с одинаковыми именами.
DocTestFailure определяет следующие атрибуты:
-
DocTestFailure.test -
Объект
DocTest, который выполнялся во время ошибки примера.
-
DocTestFailure.example -
Пример
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 получил три основных применения:
- Проверка примеров в docstring.
- Регрессионное тестирование.
- Исполняемая документация/литературное тестирование.
Эти применения имеют разные требования, и важно различать их. В частности, заполнение ваших docstring невнятными тестовыми случаями делает документацию плохой.
При написании docstring выбирайте примеры docstring с умом. В этом есть искусство, которое нужно освоить — вначале это может не даваться естественно. Примеры должны добавлять реальную ценность документации. Хороший пример часто стоит многих слов. Если это сделано тщательно, примеры будут бесценными для ваших пользователей и окупят затраченное время многократно по прошествии лет и изменении обстоятельств. Я до сих пор поражаюсь тому, как часто один из моих doctest примеров перестает работать после «безобидного» изменения.
Doctest также является отличным инструментом для регрессионного тестирования, особенно если вы не экономите на поясняющем тексте. Объединяя прозу и примеры, гораздо проще отслеживать, что на самом деле тестируется и почему. Когда тест терпит неудачу, хорошая проза может значительно упростить выявление проблемы и способ ее решения. Действительно, вы могли бы написать обширные комментарии в кодовом тестировании, но мало кто из программистов это делает. Многие обнаружили, что использование подходов doctest вместо этого приводит к гораздо более ясным тестам. Возможно, это просто потому, что doctest делает написание прозы немного проще, чем написание кода, а написание комментариев в коде немного сложнее. Думаю, дело глубже, чем просто это: естественная позиция при написании теста на основе doctest заключается в том, что вы хотите объяснить тонкости своего программного обеспечения и проиллюстрировать их примерами. Это, в свою очередь, естественным образом приводит к тестовым файлам, которые начинаются с самых простых функций и логически переходят к сложностям и граничным случаям. В результате получается связный рассказ, а не набор изолированных функций, которые тестируют изолированные фрагменты функциональности, как будто случайно. Это другой подход, и он дает другие результаты, размывая грань между тестированием и объяснением.
Регрессионное тестирование лучше всего ограничить посвященными объектами или файлами. Существует несколько вариантов организации тестов:
- Напишите текстовые файлы, содержащие тестовые случаи в виде интерактивных примеров, и протестируйте файлы с помощью
testfile()илиDocFileSuite(). Это рекомендуется, хотя проще всего сделать для новых проектов, разработанных с самого начала с использованием doctest. - Определите функции с именем
_regrtest_topic, состоящие из одной docstring, содержащей тестовые случаи для указанных тем. Эти функции можно включить в тот же файл, что и модуль, или вынести в отдельный тестовый файл. - Определите словарь
__test__, сопоставляющий темы регрессионного тестирования с docstring, содержащими тестовые случаи.
Когда вы разместили свои тесты в модуле, сам модуль может быть тестовым исполнителем. Когда тест терпит неудачу, вы можете настроить свой тестовый исполнитель на повторный запуск только неудачного doctest во время отладки проблемы. Вот минимальный пример такого тестового исполнителя:
if __name__ == '__main__':
import doctest
flags = doctest.REPORT_NDIFF|doctest.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–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/doctest.html