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