doctest
— Тестовые интерактивные примеры Python¶
Исходный код: Lib/doctest.py
Модуль doctest
выполняет поиск частей текста, которые выглядят как
интерактивные сеансы Python, а затем выполняет эти сеансы, чтобы убедиться,
что они работают точно так, как показано. Существует несколько
распространенных способов использования doctest:
- Проверка актуальности докстрингов модуля путем проверки того, что все интерактивные примеры по-прежнему работают в соответствии с документацией.
- Для проведения регрессионного тестирования, проверяя, что интерактивные примеры из тестового файла или тестового объекта работают как положено.
- Для написания учебной документации по пакету, подробно иллюстрированного примерами ввода-вывода. В зависимости от того, выделены ли примеры или текст изложения, она выглядит как «грамотное тестирование» или «исполняемая документация».
Вот полный, но небольшой модуль примерный:
"""
Это модуль "пример".
Модуль пример предоставляет одну функцию factorial(). Например,
>>> factorial(5)
120
"""
def factorial(n):
"""Вернуть факториал n, целого числа >= 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: # поймать значение как 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
!
В следующих разделах содержится полная информация. Обратите внимание,
что есть много примеров doctests в стандартном наборе тестов Python и
библиотеках. Особенно полезные примеры можно найти в стандартном файле
тестирования Lib/test/test_doctest.py
.
Простое использование: проверка примеров в докстрингах¶
Самый простой метод начать использовать doctest (рекомендуется, но
не обязательно к использованию) - завершать каждый модуль M
следующим:
if __name__ == "__main__":
import doctest
doctest.testmod()
затем doctest
проверяет докстринги в модуле M
.
Выполнение модуля в виде сценария приводит к выполнению и проверке примеров в докстринговах:
python M.py
При этом ничего не будет отображаться, если пример не завершится неудачей, и в
этом случае пример(ы) отказа и причина(ы) отказа(ов) будут напечатаны в
stdout, а завершающая строка вывода будет ***Test Failed*** N failures.
, где N - количество
неудачных примеров.
Запустите модуль с переключателем -v
:
python M.py -v
и подробный отчет о всех опробованных примерах напечатается в стандартный вывод вместе с различными резюме в конце.
Вы можете вызвать подробный режим, передав verbose=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
содержащий следующее:
Модуль ``example``
===================
Использование ``factorial``
----------------------------
Это пример текстового файла в формате reStructuredText. Сначала импортируйте
``factorial`` из модуля ``example``:
>>> from example import factorial
Теперь выполним его:
>>> factorial(6)
120
Запуск doctest.testfile("example.txt")
сообщит о найденной ошибке в документации:
File "./example.txt", line 14, in example.txt
Failed example:
factorial(6)
Expected:
120
Got:
720
Как и в случае с testmod()
, testfile()
ничего не покажет, если пример
провалился. Если пример завершается неуспешно, то пример(ы) отказа и причина(ы) отказа(ов)
печатаются в stdout с использованием того же формата, что и
testmod()
.
По умолчанию функция testfile()
выполняет поиск файлов в каталоге вызывающего
модуля. Посмотрите раздел Основной API для описания дополнительных аргументов,
которые могут быть используемы, чтобы сказать ему где искать файлы в других
местоположениях.
Как и testmod()
, подробность testfile()
может быть установлена с помощью
переключателя командной строки -v
или с дополнительным ключевым аргументом verbose.
Есть также короткий путь командной строки для управления testfile()
. Вы можете
дать указание интерпретатору Python запустить модуль doctest непосредственно
из стандартной библиотеки и передавать имя(имена) файла(ов) в командной строке:
python -m doctest -v example.txt
Поскольку имя файла не заканчивается на .py
, doctest
делает вывод,
что он должен выполняться с testfile()
, а не testmod()
.
Дополнительные сведения о testfile()
см. в разделе Основной API.
Как это работает¶
Этот раздел подробно рассказывает, как doctest работает: на какие докстринги он смотрит, как он находит интерактивные примеры, что выполнет используемый контекст, который это использует, как он обращается с исключениями и какие опциональные флаги могут быть используемый, чтобы управлять его поведением. Это информация, которую нужно знать, чтобы написать примеры doctest; для получения информации о фактическом выполнении doctest в этих примерах см. следующие разделы.
Какие докстринги исследуются?¶
Модуль docstring и все его функции, класс и метод докстрингов являются поисковыми. Объекты, импортированные в модуль, не являются поисковыми.
Кроме того, если M.__test__
существует и содержит значение true, он должен быть
словарем, и каждая запись сопоставляет имени (строки) объекту функции,
объекту класса или строки. Функция и объект класса докстрингов, найденные
в M.__test__
подвергаются просмотру, и строки рассматриваются как
докстринги. В выходных данных появляется ключ K
в M.__test__
с
именем:
<name of M>.__test__.K
Любые найденные классы рекурсивно просмотренные аналогично тестированию докстрингами, содержащихся в них методах и вложенных классах.
Детали реализации CPython: До версии 3.4 модули расширений, написанные на языке C, не полностью просматривались doctest.
Как распознаются примеры докстринга?¶
В большинстве случаев копи-и-паст интерактивной консольной сессии работает хорошо, но doctest не пытается сделать точную эмуляцию любого специфичного оболочки Python.
>>> # комментарии игнорируются
>>> 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 (исключение, трейсбэки захватываются через другое средство).
В противном случае обратная косая черта будет интерпретирована как часть строки. Например,
\n
выше будет интерпретироваться как символ новой строки. В качестве альтернативы, вы можно удвоить каждую обратную косую черту в версии doctest (и не использовать необработанную строку):>>> def f(x): ... r'''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
Этот тест завершается успешно, если поднят 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
или если пример просто печатает это текст трейсбэка. На практике обычный вывод редко начинается с строки заголовка трейсбэка, так что это не создает реальных проблем. - У каждой строки стека трейсбэка (если есть) должен быть остступ больше, чем у первой строки примера, или начинаются с неалфавитно-цифрового символа. Первая строка после заголовка трассировки содержит тот же отступ и начинается с алфавитно-цифровым началом предоставленной детализации исключения. Конечно, это делает правильные вещи для подлинных трейсбэков.
- Когда определена опция
IGNORE_EXCEPTION_DETAIL
doctest, все после крайнего левого двоеточия и любой информации о модуле в имени исключения игнорируется. - Интерактивная оболочка пропускает строку заголовка трейсбэк для некоторых
SyntaxError
ов. Но doctest использует строку заголовка трейсбэк, чтобы отличать исключения от неисключений. Поэтому в редких случаях, когда необходимо протестироватьSyntaxError
, в котором отсутствует заголовок трейсбэка, необходимо вручную добавить строку заголовка трейсбэка в тестовый пример.
Для некоторого
SyntaxError
Python показывает позицию символа синтаксической ошибки, используя маркер^
:>>> 1 1 File "<stdin>", line 1 1 1 ^ SyntaxError: invalid syntax
Поскольку строки, показывающие положение ошибки, находятся перед типом особой ситуации и подробностями, они не проверяются доктестом. Например, следующий тест будет пройден, даже если он поместит маркер
^
в неправильное место:>>> 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.
DONT_ACCEPT_BLANKLINE
¶ По умолчанию, если ожидаемый выходной блок будет содержать строку, содержащую только строку
<BLANKLINE>
, то эта строка будет соответствовать пустой строке в фактическом выводе. Поскольку действительная пустая строка ограничивает ожидаемый вывод, это единственный метод сообщить, что ожидается пустая строка. Если указан параметрDONT_ACCEPT_BLANKLINE
, эта замена не разрешена.
-
doctest.
NORMALIZE_WHITESPACE
¶ При определении последовательности из всех пробелов (пробелы и новые строки) рассматриваются как равные. Любая последовательность пробелов в ожидаемом выводе будет соответствовать любой последовательности пробелов в фактическом выводе. По умолчанию пробел должен точно соответсвовать.
NORMALIZE_WHITESPACE
особенно полезна, когда строка ожидаемого вывода очень длинна, и вы хотите обернуть ее в несколько строк в источнике.
-
doctest.
ELLIPSIS
¶ Если указан маркер эллипсиса (
...
) в ожидаемом выводе может соответствовать любой подстроке в фактическом выводе. Он включает подстроки, которые охватывают границы строк, и пустые подстроки, поэтому лучше всего использовать его по минимуму. Сложное использование может привести к одним и тем же видам «упс, он слишком много соответствовал!» сюрпризов, к которым.*
склонен в регулярных выражениях.
-
doctest.
IGNORE_EXCEPTION_DETAIL
¶ Если указано, наример ожидаемое исключение проходит в случае возникновения исключения ожидаемого типа, даже если детали исключения не совпадают. Например, ожидаемое
ValueError: 42
пройдёт, если фактическое вызванное исключение равноValueError: 3*14
, но потерпит неудачу, при возникновенииTypeError
.Он также игнорирует имя модуля используемый в отчетах doctest Python 3. Следовательно, оба этих варианта будут работать с указанным флагом, независимо от этого:
>>> raise CustomError('message') Traceback (most recent call last): CustomError: message >>> raise CustomError('message') Traceback (most recent call last): my_module.CustomError: message
Обратите внимание, что
ELLIPSIS
может также применяться для игнорирования подробностей сообщения об исключении, но такой тест все равно может потерпеть неудачу на основе того, напечатаны ли детали модуля как часть имени исключения. ИспользованиеIGNORE_EXCEPTION_DETAIL
и подробностей из Python 2.3 также является единственным понятным способом написать доктест, который не заботится о деталях исключения, но продолжает проходить под Python 2.3 или ранее (эти выпуски не поддерживают doctest директивы и игнорируют их как неактуальные комментарии). Например:>>> (1, 2)[3] = 'moo' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: object doesn't support item assignment
проходит для Python 2.3 и более поздних Python версий с указанным флагом, даже несмотря на то, что подробности изменения в Python 2.4 говорят «does not» вместо «doesn’t».
Изменено в версии 3.2:
IGNORE_EXCEPTION_DETAIL
теперь также игнорирует любую информацию, относящуюся к модулю, содержащему тестируемое исключение.
-
doctest.
SKIP
¶ Если указано, не запускать пример вообще. Это может быть полезно в контекстах, где примеры doctest служат как документацией, так и контрольными примерами, и пример должен быть включен для целей документации, но не должен проверяться. Например, выходные данные примера могут быть случайными; или пример может зависеть от ресурсов, которые будут недоступны для тестов.
Флаг SKIP может также быть используемый для того, чтобы временно «прокомментировать» примеры.
-
doctest.
COMPARISON_FLAGS
¶ Битовая маска «или» для объединение всех вышеуказанных флагов сравнения.
Вторая группа параметров управляет тем, как регистрируются сбои тестирования:
-
doctest.
REPORT_UDIFF
¶ Если указано, то отказы, связанные с многострочными ожидаемыми и фактическими выходами, отображается с помощью унифицированного diff.
-
doctest.
REPORT_CDIFF
¶ Если указано, отказы, связанные с многострочными ожидаемыми и фактическими выходами будет отображаться с помощью контекстного diff.
-
doctest.
REPORT_NDIFF
¶ Если указано, различия вычисляются методом
difflib.Differ
, используя тот же алгоритм, что и популярная утилитаndiff.py
. Это единственный метод, который маркирует различия как внутри строк, так и между ними. Например, если строка ожидаемого вывода содержит цифру1
, где фактический вывод содержит буквуl
, то вставляется строка с курсором, обозначающим несоответствующие позиции столбца.
-
doctest.
REPORT_ONLY_FIRST_FAILURE
¶ Если указано, выводит первый неуспешный пример в каждом доктесте, но подавляет вывод для всех остальных примеров. Он предотвратит появлению отчетов доктестов правильных примеров, сломанных из-за предыдущих сбоев; но это может также скрыть неправильные примеры, которые обрушиваются независимо от первого сбоя. При указании параметра
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 после примера исходного кода:
doctest ::= «#» «doctest»: 'directive_options' directive_options ::= 'directive_option' («» 'directive_option') \* directive_option ::= 'on_or_off' 'directive_option_name' on_or_off: «+» \| «-» directive_option_name ::= «DONT_ACCEPT_BLANKLINE» \| «NORMALIZE_WHITESPACE» \|...
Между +
или -
и именем параметра директивы не допускается
пробел. Имя опции директивы может быть любым из имен опций флагов, описанных
выше.
Директивы doctest примера изменяют поведение doctest для этого примера.
Используйте команду +
, чтобы включить именованное поведение, или
команду -
, чтобы отключить его.
Например, этот тест проходит успешно:
>>> print(list(range(20)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Без директивы он не удастся, потому что фактический вывод не содержит двух пробелов перед одноразрядными элементами списка, так и потому, что фактический вывод находится в одной строке. Этот тест также проходит и для него также требуется директива:
>>> print(list(range(20)))
[0, 1, ..., 18, 19]
Несколько директив могут быть используемый на единственной физической строки, отделенной запятыми:
>>> print(list(range(20)))
...
[0, 1, ..., 18, 19]
Если для одного примера используются несколько директив комментариев, то они комбинируются:
>>> print(list(range(20)))
...
[0, 1, ..., 18, 19]
Как показано в предыдущем примере, в пример можно добавить строки ...
,
содержащие только директивы. Это может быть полезно, когда пример слишком
длинный для директивы, чтобы удобно поместиться на одной и той же строке:
>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40)))
...
[0, ..., 4, 10, ..., 19, 30, ..., 39]
Обратите внимание, что, по умолчанию все опции отключены, а директивы
применимы только к тому примеру, в котором они представлены, включение опций
(через +
в директиве) обычно является единственным значимым выбором.
Однако флаги опций могут также передаваться функциям, выполняющим doctests,
устанавливая различные значения по умолчанию. В таких случаях может быть полезно
в директиве отключить опцию через -
.
Предупреждения¶
doctest
серьезно относится к необходимости точного совпадения ожидаемых
результатов. Если даже один символ не совпадает, тест завершается неудачей.
Это, вероятно, удивит вас несколько раз, поскольку вы узнаете, что Python не даёт
точной гарантии выхода. Например, при печати множества, Python
не гарантирует, что элемент напечатан в каком-либо определенном порядке, так что
тест:
>>> foo()
{"Hermione", "Harry"}
это уязвимо! Один обходной путь состоит в том, чтобы сделать вместо
>>> foo() == {"Hermione", "Harry"}
True
выполнить:
>>> d = sorted(foo())
>>> d
['Harry', 'Hermione']
Примечание
До Python 3.6, при печати словаря, Python не гарантировал, что пары ключ-значение были напечатаны в каком-либо конкретном порядке.
Есть и другие примеры, но вы поняли идею.
Другая плохая идея - печатать что-либо, которые встраивают адрес объекта, например:
>>> id(1.0) # наверняка в какой-то момент потерпит неудачу
7948648
>>> class C: pass
>>> C() # функция repr() по умолчанию для экземпляров вставляет адрес
<__main__.C instance at 0x00AC18F0>
Директива ELLIPSIS
предлагает хороший подход для последнего примера:
>>> C()
<__main__.C instance at 0x...>
Числа с плавающей запятой также подвержены небольшим изменениям выходных данных на разных платформах, поскольку Python переносится на библиотеку платформы C для форматирования с плавающей запятой, и библиотеки C сильно различаются по качеству:
>>> 1./7 # рискованный
0.14285714285714285
>>> print(1./7) # безопаснее
0.142857142857
>>> print(round(1./7, 6)) # гораздо безопаснее
0.142857
Числа в формы I/2.**J
безопасны на всех платформах, и я часто изобретаю примеры
доктестов для получения чисел этой формы:
>>> 3./4 # совершенно безопасно
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
, никакие дополнительные globals не используются. Это расширенная функция, позволяющая параметризовать доктесты. Например, доктест может быть написан для базового класса, используя родное имя класса, затем снова использоваться чтобы проверить любое количество подклассов, передав extraglobs словарь, отображающий родное имя подкласса, которое будет проверено.Дополнительный аргумент verbose печатает много подробностей, если True, и печатает только ошибки, если false; по умолчанию или, если
None
, это верно тогда и только тогда, когда'-v'
присутствует вsys.argv
.Необязательный аргумент report печатает сводку в конце, когда true, иначе ничего не печатает в конце. В подробном режиме сводка является подробной, иначе сводка очень краткая (фактически пуста, если все тесты прошли).
Необязательный аргумент optionflags (по умолчанию значение 0) принимает побитовое или флагов параметров. См. раздел Флаги выбора.
Необязательный аргумент raise_on_error по умолчанию содержит значение false. Если значение равно true, при первом сбое или при непредвиденном исключении в примере возникает исключение. Это позволяет выполнять отладку отказов после ошибок. Поведение по умолчанию - продолжение выполнения примеров.
Необязательный аргумент parser указывает
DocTestParser
(или подкласс), который должен быть применен для извлечения тестов из файлов. По умолчанию используется обычный парсер (т.е.DocTestParser()
).Необязательный аргумент encoding указывает кодировку, которая должена быть используема для преобразования файла в Юникод.
- Если 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.master.summarize()
вместе сtestmod()
продолжил получение вывода для объектов без тестов. Аргумент exclude_empty для более нового конструктораDocTestFinder
по умолчанию содержит значение true.Дополнительные аргументы extraglobs, verbose, report, optionflags, raise_on_error и globs совпадают с функцией
testfile()
выше, за исключением того, что по умолчанию globs равенm.__dict__
.
-
doctest.
run_docstring_examples
(f, globs, verbose=False, name="NoName", compileflags=None, optionflags=0)¶ Примеры тестов, связанные с объектом f; например, f может быть строкой, модулем, функцией или объектом класса.
Неглубокая копия аргумента словаря globs используется для выполнения контекста.
Дополнительный аргумент name используется в сообщениях об ошибках и содержит значение по умолчанию
"NoName"
.Если необязательный аргумент verbose содержит значение true, выходные данные генерируются даже при отсутствии ошибок. По умолчанию выходные данные генерируются только в случае отказа примера.
Дополнительный аргумент compileflags задает набор флагов, которые должны быть используемы компилятором Python при запуске примеров. По умолчанию или, если
None
, флаги выводятся в соответствии с набором будущих элементов, найденных в globs.Дополнительный аргумент optionflags работает как для функции
testfile()
выше.
Unittest API¶
По мере роста вашей коллекции доктестируемых модулей вам будет нужен способ
систематизации выполнения всех доктестов. doctest
обеспечивает две
функции, которые могут быть используемый, чтобы создать наборы тестов unittest
из модулей и текстовых файлов, содержащих доктестов. Для интеграции с
обнаружением теста unittest
включите функцию load_tests()
в модуль
тестирования:
import unittest
import doctest
import my_module_with_doctests
def load_tests(loader, tests, ignore):
tests.addTests(doctest.DocTestSuite(my_module_with_doctests))
return tests
Существует две основные функции для создания unittest.TestSuite
сущности из
текстовых файлов и модулей с доктестами:
-
doctest.
DocFileSuite
(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None)¶ Преобразование тестов doctest из одного или нескольких текстовых файлов в
unittest.TestSuite
.Возвращаемый
unittest.TestSuite
запускает фреймворк unittest и управлять интерактивными примерами в каждом файле. Если пример в каком-либо файле завершается неуспешно, то тест синтезированного блока завершается неуспешно, и возникает исключениеfailureException
, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.Передайте один или несколько путей (как строки) текстовым файлам, которые необходимо проверить.
Параметры могут быть предоставлены в виде ключевых аргументов:
Необязательный аргумент module_relative указывает, как следует интерпретировать имена файлов в paths:
- Если module_relative содержит значение
True
(по умолчанию), то каждое имя файла в paths указывает путь к модулю, не зависящий от ОС. По умолчанию этот путь относится к каталогу вызывающего модуля; но если указан аргумент package, то он относится к этому пакету. Для обеспечения независимости от ОС каждое имя файла должно использовать символы/
для разделения сегментов пути и не может быть абсолютным путем (т.е. оно может начинаться не с/
). - Если module_relative является
False
, то каждое имя файла в paths указывает путь, специфичный для ОС. Путь может быть абсолютным или относительным; относительные пути разрешаются относительно текущей рабочей папки.
Необязательный аргумент package - это пакет Python или имя пакета Python, каталог которого должен быть использован в качестве базового каталога для имен файлов, относящихся к модулю в paths. Если никакой пакет не определен, то каталог запрашиваемого модуля - используется как основной каталог для относительно модуля имен файлов. Ошибка при указании package, если module_relative является
False
.Дополнительный аргумент setUp указывает функцию настройки для набора тестов. Это вызывается перед выполнением тестов в каждом файле. Функция setUp будет передана объекту
DocTest
. Функция установки может получить доступ к клобальному тесту, когда передан globs атрибут теста.Дополнительный аргумент tearDown указывает функцию выделения для набора тестов. Это вызывается после выполнения тестов в каждом файле. Функция tearDown будет передана объекту
DocTest
. Функция установки может получить доступ к клобальному тесту, когда передан globs атрибут теста.Необязательный аргумент globs - это словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию globs является новым пустым словарем.
Необязательный аргумент optionflags указывает параметры doctest по умолчанию для тестов, созданные отдельными флагами опций. См. раздел Флаги выбора. Для получения более эффективного способа настройки параметров отчетности см. функцию
set_unittest_reportflags()
ниже.Необязательный аргумент parser указывает
DocTestParser
(или подклассу), который используется для извлечения тестов из файлов. По умолчанию используется обычный парсер (т.е.DocTestParser()
).Необязательный аргумент encoding указывает кодировку, которая должена быть используема для преобразования файла в Юникод.
Глобальная
__file__
добавляется к глобалям, предоставляемым для тестирования, загружаемого из текстового файла с помощьюDocFileSuite()
.- Если module_relative содержит значение
-
doctest.
DocTestSuite
(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None)¶ Преобразование тестов доктестов для модуля в
unittest.TestSuite
.возвращенный:class:unittest.TestSuite должен управлять unittest фреймворк и управляет каждым doctest в модуле. Если какой-либо из тестов не проходит, то тест синтезированного блока завершается неуспешно, и возникает исключение
failureException
, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.Дополнительный аргумент module предоставляет модуль для тестирования. Это может быть объект модуля или (возможно, пунктирное) имя модуля. Если не указан, модуль, вызывающий эту функцию, является используемый.
Необязательный аргумент globs - это словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию globs является новым пустым словарем.
Дополнительный аргумент extraglobs указывает дополнительный набор глобальных переменных, который объединяется в globs. По умолчанию никакие дополнительные globals не используемый.
Дополнительный test_finder аргумента - объект
DocTestFinder
(или общедоступная замена), который является используемый, чтобы извлечь doctests из модуля.Необязательные аргументы 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 принимает побитовое ИЛИ флагов параметров. См. раздел Флаги выбора. Только «отчетные флаги» могут быть используемы.
Это глобальный параметр модуля и влияет на все будущие доктесты, выполняемые модулем
unittest
: методrunTest()
DocTestCase
просматривает флаги опций, заданные для тестового случая при построенииDocTestCase
сущности. Если флаги отчетов не были определены (является типичным и ожидаемым случаем), флаги сообщенияdoctest
unittest
- побитово ИЛИ с опциональными флагами, и дополнительными опциональными флагами передаютсяDocTestRunner
сущности, созданной, чтобы управлять doctest. Если какие-либо флаги отчетов были определены, когда экземплярDocTestCase
был создан, флаги отчетовdoctest
unittest
игнорируются.Значение флагов отчетов
unittest
влияют перед вызовом функции, возвращаемой функцией.
Продвинутый API¶
Базовый API представляет собой простую обертку, предназначенную для упрощения использования doctest. Она является достаточно гибкой и должна удовлетворять потребности большинства пользователей; однако, если требуется более тонкий контроль за тестированием или требуется расширить возможности doctest, следует использовать продвинутое API.
Продвинутое API вращается вокруг двух контейнерных классов, которые применяются для хранения интерактивных примеров, извлеченных из doctest примеров:
Example
: одна Python инструкция, в паре с ожидаемым выводом.DocTest
: коллекцияExample
ов, как правило, извлеченная из единственного докстринга или текстового файла.
Дополнительные классы обработки определяются для поиска, анализа, выполнения и проверки примеров doctest:
DocTestFinder
: находит все докстринги в данном модуле и используетDocTestParser
для созданияDocTest
из каждого докстринга, содержащего интерактивные примеры.DocTestParser
: создает объектDocTest
из строки (такого как докстринг объект).DocTestRunner
: выполняет примеры вDocTest
и используетOutputChecker
для проверки их выходных данных.OutputChecker
: сравнение фактических выходных данных из примера doctest с ожидаемыми выходными данными и определения их соответствия.
Отношения между этими классами обработки суммированы на следующей диаграмме:
list of:
+------+ +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+ | ^ +---------+ | ^ (printed)
| | | Example | | |
v | | ... | v |
DocTestParser | Example | OutputChecker
+---------+
Объекты DocTest¶
-
class
doctest.
DocTest
(examples, globs, name, filename, lineno, docstring)¶ Коллекция примеров doctest, которые должны выполняться в одном пространстве имен. Аргументы конструктора используются, чтобы инициализировать атрибуты тех же имен.
DocTest
определяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.-
examples
¶ Список
Example
объектов кодирующих отдельные интерактивные примеры Python, которыми должены быть выполнены этим тестом.
-
globs
¶ Пространство имен (или глобалы), в котором должны выполняться примеры. Это словарь, отображающий имена на значения. Любые изменения пространства имен, сделанного примерами (такими как биндинг новых переменных), будут отражены в
globs
после того, как тест будет запущен.
-
name
¶ Строковое имя, идентифицирующее
DocTest
. Обычно это имя объекта или файла, из которого был извлечен тест.
-
filename
¶ Название файла, из которого был извлечен этот
DocTest
; илиNone
, если имя файла неизвестно, или еслиDocTest
не был извлечен из файла.
-
lineno
¶ Номер строки в
filename
, гдеDocTest
начинается, илиNone
, если номер строки недоступен. Этот номер строки отсчитывается от нуля относительно начала файла.
-
docstring
¶ Строка, из которой был извлечен тест, или
None
, если строка недоступена, или если тест не был извлечен из строки.
-
Объекты Example¶
-
class
doctest.
Example
(source, want, exc_msg=None, lineno=0, indent=0, options=None)¶ Единоличный интерактивный пример, состоящий из Python инструкции и ожидаемого вывода. Используемые аргументы конструктора для инициализации атрибутов с теми же именами.
Example
определяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.-
source
¶ Строка, содержащая исходный код примера. Этот исходный код состоящий из одной Python инструкции, и всегда заканчивается новой строкой; при необходимости конструктор добавляет новую строку.
-
want
¶ Ожидаемый вывод от запуска исходного кода примера (либо от stdout, либо от трейсбэка в случае исключения).
want
заканчивается символом новой строки, если никакой вывод не ожидается, в этом случае это - пустая строка. При необходимости конструктор добавляет новую строку.
-
exc_msg
¶ Сообщение об исключении, созданное в примере, если ожидается, что пример создаст исключение; или
None
, если не предполагается создание исключения. Это сообщение об исключении сравнивается с возвращаемым значениемtraceback.format_exception_only()
.exc_msg
заканчивается символом новой строки, если она неNone
. При необходимости конструктор добавляет символ новой строки.
-
lineno
¶ Номер строки в строке, содержащей этот пример, где пример начинается. Номер строки начинается с нуля относительно начала содержащего строку.
-
indent
¶ Отступы примера в содержащейся строке, т.е. число символов пробела, предшествующее первому приглашению (>>>) примера.
-
options
¶ Словарь, отображающий опциональные флаги в
True
илиFalse
, который используется для переопределения параметров по умолчанию для этого примера. Флаги любых опций, не содержавшиеся в этом словаре, остаются в их значениях по умолчанию (как определеноDocTestRunner
optionflags
). По умолчанию параметры не установлены.
-
Объекты DocTestFinder¶
-
class
doctest.
DocTestFinder
(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)¶ Класс обработки используемый для извлечения
DocTest
ов, относящихся к данному объекту, из его докстрингов и докстрингов содержащихся в нем объектов.DocTest
s можно извлечь из модулей, классов, функций, методов, статических методов, классметодов и свойств.Опциональным аргументом 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 не определен.
- Чтобы предотвратить извлечение DocCreateFinder из DocTests объектов, импортированных из других модулей. (Содержащиеся объекты с модулями, отличными от 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
. Номера строк начинаются с 0. Необязательный аргумент name является именем, идентифицирующим эту строку и используется только для сообщений об ошибках.
-
parse
(string, name='<string>')¶ Разделить переданную строку на примеры и промежуточный текст и возвращает её как список чередующихся
Example
ов и строк. Номера строк дляExample
начинаются с 0. Необязательный аргумент name является именем, идентифицирующим эту строку и используется только для сообщений об ошибках.
-
Объекты DocTestRunner¶
-
class
doctest.
DocTestRunner
(checker=None, verbose=None, optionflags=0)¶ Класс обработки используемый для выполнения и проверки интерактивных примеров в
DocTest
.Сравнение ожидаемых и фактических результатов осуществляется с помощью
OutputChecker
. Это сравнение может быть настроено с помощью ряда флагов опций; для получения дополнительной информации см. раздел Флаги выбора. Если флагов опций недостаточно, сравнение также может быть настроено путем передачи конструктору подклассаOutputChecker
.Отображением вывода тестового раннера можно управлять двумя способами. Во-первых, выходная функция может быть передана в
TestRunner.run()
; эта функция будет вызвана с строкой, которую нужно показать. По умолчанию используется значениеsys.stdout.write
. Если захват выходных данных недостаточен, то вывод на экран также можно настроить с помощью подкласса DocCreateRunner и переопределения методовreport_start()
,report_success()
,report_unexpected_exception()
иreport_failure()
.Дополнительный checker ключевой аргумент определяет объект
OutputChecker
(или вставная замена), который применяется для сравнения ожидаемого вывода с фактическим выводом doctest примеров.Дополнительный ключевой аргумент verbose управляет детализацией
DocTestRunner
. Если verbose -True
, то информация печатается о каждом примере, так как он выполняется. Если verbose являетсяFalse
, то печатаются только отказы. Если verbose неуказанн, илиNone
, то производится подробный вывод тогда и только тогда, когда установлен переключатель командной строки-v
.Дополнительный optionflags ключевой аргумент может быть используем для управления, как тестраннер сравнивает ожидаемый вывод с фактическим выводом, и как он покажет сбои. Дополнительные сведения см. в разделе Флаги выбора.
DocTestParser
определяет следующие методы:-
report_start
(out, test, example)¶ Сообщить, что тестраннер собирается обработать данный пример. Этот метод предназначен для того, чтобы позволить подклассам
DocTestRunner
настраивать их вывод; он не должен вызываться напрямую.example - пример, который будет обработан. test - тест содержащий пример. out - выходная функция, переданная
DocTestRunner.run()
.
-
report_success
(out, test, example, got)¶ Сообщить, что данный пример выполнен успешно. Этот метод предназначен для того, чтобы позволить подклассам
DocTestRunner
настраивать их вывод; он не должен вызываться напрямую.example - пример, который будет обработан. got является фактическим выводом из примера. test - тест, содержащий пример. out - выходная функция, переданная
DocTestRunner.run()
.
-
report_failure
(out, test, example, got)¶ Сообщить, что данный пример не удался. Этот метод предназначен для того, чтобы позволить подклассам
DocTestRunner
настраивать их вывод; он не должен вызываться напрямую.example - пример, который будет обработан. got является фактическим выводом из примера. test - тест, содержащий пример. out - выходная функция, переданная
DocTestRunner.run()
.
-
report_unexpected_exception
(out, test, example, exc_info)¶ Сообщить, что данный пример вызвал непредвиденное исключение. Этот метод предназначен для того, чтобы позволить подклассам
DocTestRunner
настраивать их вывод; он не должен вызываться напрямую.example - пример, который будет обработан. exc_info - кортеж, содержащий информацию о неожиданном исключении (как возвращенный
sys.exc_info()
). test - тест, содержащий пример. 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)¶ Распечатать сводку всех тестовых примеров, которые были выполнены в этом DocCreateRunner, и возвратить именованный кортеж
TestResults(failed, attempted)
.Необязательный аргумент verbose определяет, насколько подробна сводка. Если детализация не указана, то используется подробность
DocTestRunner
.
-
Объекты OutputChecker¶
-
class
doctest.
OutputChecker
¶ Класс, применяется для проверки соответствия фактических выходных данных из примера doctest с ожидаемыми выходными данными.
OutputChecker
определяет два метода:check_output()
, который сравнивает заданную пару выводов, и возвращаетTrue
, если они совпадаются; иoutput_difference()
, который возвращает строку, описывающую различия между двумя выводами.OutputChecker
определяет следующие методы:-
check_output
(want, got, optionflags)¶ Возвращает
True
тогда и только тогда, когда фактический вывод примера (got) соответствует ожидаемой выводу (want). Строки всегда считаются совпадающими, если они идентичны; но в зависимости от того, какие флажки используются тестраннером, возможно также несколько неточных типов совпадений. Дополнительные сведения о флагах параметров см. в разделе Флаги выбора.
-
output_difference
(example, got, optionflags)¶ Возвращает строку описывающую различия между ожидаемым выводом для данного примера (example) и фактическим выводом (got). optionflags - множество опций флагов, используемых для сравнения want и got.
-
Отладка¶
Doctest предоставляет несколько механизмов для отладки примеров doctest:
Несколько функций преобразуют доктесты в исполняемые программы Python, которые можно запустить в отладчике Python
pdb
.Класс
DebugRunner
- это подклассDocTestRunner
, который вызывает исключение для первого неуспешного примера, содержащего информацию об этом примере. Эта информация может быть используема для выполнения отладки после ошибки в примере.Случаи
unittest
, произведенныеDocTestSuite()
, поддерживают методdebug()
, определенныйunittest.TestCase
.В примере doctest можно добавить вызов
pdb.set_trace()
, и при выполнении этой строки вы перейдете в отладчик 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) >>>
Функции, которые преобразуют доктесты в 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 - это объект модуля или пунктирное имя модуля, содержащее объект, чьи доктесты представляют интерес. Аргумент name - это имя (в модуле) объекта с интересующими доктестами. Результатом является строка, содержащая докстринг объекта, преобразованная в сценарий Python, как описано для
script_from_examples()
выше. Например, если модульa.py
содержит функцию верхнего уровняf()
, то:import a, doctest print(doctest.testsource(a, "a.f"))
печатает скриптовую версию функции
f()
докстринга, с доктестами преобразованными в код, а остальное помещается в комментарии.
-
doctest.
debug
(module, name, pm=False)¶ Отладка доктестов для объекта.
Аргументы module и name такие же, как и для функции
testsource()
выше. Синтезированный сценарий Python для докстринга именованного объекта записывается во временный файл, а затем этот файл запускается под управлением отладчика Pythonpdb
.Используется поверхностная копия
module.__dict__
для локального и глобального контекста выполнения.Дополнительный аргумент pm определяет, нужно ли использовать отладку после крушения сценария. Если pm содержит значение True, файл сценария запускается непосредственно и отладчик подключается только в том случае, если сценарий завершается путем вызова необработанного исключения. Если это так, то отладка после крушения вызывается через
pdb.post_mortem()
, передавая объект трейсбэка из необработанного исключения. Если pm не указан или содержит значение False, сценарий запускается в отладчике с самого начала посредством передачи соответствующего вызоваexec()
pdb.run()
.
-
doctest.
debug_src
(src, pm=False, globs=None)¶ Отладка доктестов в строка.
Аналогична функции
debug()
выше, за исключением того, что строка, содержащий примеры doctest, определяется непосредственно через аргумент src.Необязательный аргумент pm содержит аналогичное значение, что и в функции
debug()
выше.Дополнительный аргумент globs предоставляет словарь для использования в качестве локального и глобального контекста выполнения. Если не указан, или
None
, используется пустой словарь. Если указан, то используется поверхностная копия словаря.
Класс DebugRunner
и специальные исключения, которые он может поднять,
представляют нибольший интерес для авторов тестов фреймворка и будут
только коротко изложены здесь. Посмотрите исходный код и особенно
DebugRunner
докстринг (который является doctest!) для более подробной
информацией:
-
class
doctest.
DebugRunner
(checker=None, verbose=None, optionflags=0)¶ Подкласс
DocTestRunner
, который поднимает исключение, как только обнаруживается ошибка. При возникновении непредвиденного исключения, возникает исключениеUnexpectedException
, содержащее тест, пример и исходное исключение. Если выходные данные не совпадают, возникает исключениеDocTestFailure
, содержащее тест, пример и фактический вывод.Для получения информации о параметрах и методах конструктора см. документацию по
DocTestRunner
в разделе Продвинутый API.
Сущность DebugRunner
может поднять два исключения:
-
exception
doctest.
DocTestFailure
(test, example, got)¶ Исключение, поднимаемое
DocTestRunner
для сигнализации о том, что фактические выходные данные doctest примера не соответствуют ожидаемым выходным данным. Аргументы конструктора используются для инициализации атрибутов с теми же именами.
DocTestFailure
определяет следующие атрибуты:
-
DocTestFailure.
got
¶ Фактический вывод примера.
-
exception
doctest.
UnexpectedException
(test, example, exc_info)¶ Исключение, вызванное
DocTestRunner
, чтобы показать, что пример doctest вызвал непредвиденное исключение. Аргументы конструктора используются для инициализации атрибутов с теми же именами.
UnexpectedException
определяет следующие атрибуты:
-
UnexpectedException.
exc_info
¶ Кортеж, содержащий сведения о непредвиденном исключении, возвращаемого
sys.exc_info()
.
Мыльница¶
Как отмечалось во введении, у модуля doctest
три основных применения:
- Проверка примеров в докстрингах.
- Регрессионное тестирование.
- Исполняемая документация/грамотное тестирование.
У этив видов применений различные требования, и их важно различать. В частности, заполнение ваших докстрингов непонятными тестовыми примерами создает плохую документацию.
При написании докстрингов, тщательно выберите примеры с докстрингами. Это
искусство, которое должно быть поначалу изучено и он может быть неестественно
на первых порах. Примеры должны добавлять подлинное значение в документацию.
Хороший пример часто многословен. Если делать это с осторожностью, примеры
будут бесценны для ваших пользователей, и многократно окупит время, необходимое для их
сбора и изменения в течение многих лет. Я до сих пор поражаюсь тем, как
часто один из моих doctest
примеров перестает работать после «безобидной»
перемены.
Doctest также является отличным инструментом для регрессионного тестирования, особенно если вы не скупитесь на пояснительный текст. Перемежая прозу и примеры, становится намного проще отслеживать то, что на самом деле тестируется, и почему. Когда тест проваливается, хорошая проза может значительно облегчить выяснение того, в чем проблема и как она должна быть исправлена. Это правда, что вы могли бы написать обширные комментарии в тестировании на основе кода, но мало кто из программистов это делает. Многие обнаружили, что использование doctest подходов вместо этого приводит к гораздо более четким тестам. Возможно, это просто потому, что doctest писать немного проще, чем код, в то время как писать комментарии в код немного сложнее. Я думаю, что дело обстоит глубже: естественное отношение при написании теста на основе doctest заключается в том, что вы хотите объяснить тонкие моменты вашего программного обеспечения и проиллюстрировать их примерами. Это, в свою очередь, естественно приводит к тестовым файлам, которые начинаются с простейших функций и логически переходят к сложностям и краевым случаям. Связанное повествование - это результат, вместо набора изолированных функций, которые тестируют изолированные биты функциональности, кажущиеся случайными. Это другое отношение и дает разные результаты, размывая различие между тестированием и объяснением.
Регрессионное тестирование лучше всего ограничить выделенными объектами или файлами. Существует несколько вариантов организации тестов:
- Запись текстовых файлов, содержащих тестовые примеры, в качестве интерактивных
примеров и тестирование файлов с помощью
testfile()
илиDocFileSuite()
. Это рекомендуется, хотя проще всего сделать для новых проектов, разработанных с самого начала для использования doctest. - Определите функции с именами
_regrtest_topic
, состоящие из отдельных докстрингов, содержащих контрольные примеры для названных тем. Эти функции могут быть включены в тот же файл, что и модуль, или разделены на отдельный тестовый файл. - Определите отображение словаря
__test__
из тем регрессионного теста в докстринги, содержащие контрольные примеры.
После размещения тестов в модуле сам модуль может быть тестранером. Если тест завершается неуспешно, можно выполнить повторный запуск только неудачного теста во время отладки проблемы. Вот минимальный пример такого тестранера:
if __name__ == '__main__':
import doctest
flags = doctest.REPORT_NDIFF|doctest.FAIL_FAST
if len(sys.argv) > 1:
name = sys.argv[1]
if name in globals():
obj = globals()[name]
else:
obj = __test__[name]
doctest.run_docstring_examples(obj, globals(), name=name,
optionflags=flags)
else:
fail, total = doctest.testmod(optionflags=flags)
print("{} failures out of {} tests".format(fail, total))
Сноски
[1] | Примеры, содержащие как ожидаемый вывод, так и исключение, не поддерживаются. Попытка угадать, где один заканчивается, а другой начинается, слишком подвержена ошибкам, и это также запутывает тест. |