doctest
— Протестируйте интерактивные примеры на Python¶
Исходный код: Lib/doctest.py
Модуль doctest
выполняет поиск фрагментов текста, которые выглядят как интерактивные сеансы Python, а затем выполняет эти сеансы, чтобы убедиться, что они работают точно так, как показано на рисунке. Существует несколько распространенных способов использования doctest:
Чтобы проверить актуальность строк документации модуля, убедитесь, что все интерактивные примеры по-прежнему работают в соответствии с документацией.
Выполнить регрессионное тестирование, проверив, что интерактивные примеры из тестового файла или тестового объекта работают должным образом.
Написать обучающую документацию для пакета, щедро иллюстрированную примерами ввода-вывода. В зависимости от того, выделены ли примеры или пояснительный текст, это может быть названо «грамотным тестированием» или «исполняемой документацией».
Вот полный, но небольшой пример модуля:
"""
This is the "example" module.
The example module supplies one function, factorial(). For example,
>>> factorial(5)
120
"""
def factorial(n):
"""Return the factorial of n, an exact integer >= 0.
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> factorial(30)
265252859812191058636308480000000
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n must be >= 0
Factorials of floats are OK, but the float must be an exact integer:
>>> factorial(30.1)
Traceback (most recent call last):
...
ValueError: n must be exact integer
>>> factorial(30.0)
265252859812191058636308480000000
It must also not be ridiculously large:
>>> factorial(1e100)
Traceback (most recent call last):
...
OverflowError: n too large
"""
import math
if not n >= 0:
raise ValueError("n must be >= 0")
if math.floor(n) != n:
raise ValueError("n must be exact integer")
if n+1 == n: # catch a value like 1e300
raise OverflowError("n too large")
result = 1
factor = 2
while factor <= n:
result *= factor
factor += 1
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
Если вы запустите example.py
непосредственно из командной строки, doctest
сотворит свое волшебство:
$ python example.py
$
Результата нет! Это нормально, и это означает, что все примеры сработали. Передайте -v
скрипту, и doctest
напечатает подробный журнал того, что он пытается выполнить, и в конце напечатает сводку:
$ python example.py -v
Trying:
factorial(5)
Expecting:
120
ok
Trying:
[factorial(n) for n in range(6)]
Expecting:
[1, 1, 2, 6, 24, 120]
ok
И так далее, в конце концов заканчивая:
Trying:
factorial(1e100)
Expecting:
Traceback (most recent call last):
...
OverflowError: n too large
ok
2 items passed all tests:
1 tests in __main__
8 tests in __main__.factorial
9 tests in 2 items.
9 passed and 0 failed.
Test passed.
$
Это все, что вам нужно знать, чтобы начать продуктивно использовать doctest
! Переходите к работе. В следующих разделах приведены подробные сведения. Обратите внимание, что в стандартном наборе тестов и библиотеках Python есть много примеров тестов doctests. Особенно полезные примеры можно найти в стандартном тестовом файле Lib/test/test_doctest/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 на этих примерах, смотрите в следующих разделах.
Какие Строки Документации Проверяются¶
Выполняется поиск по строке документации модуля, а также по всем строкам документации функций, классов и методов. Поиск по объектам, импортированным в модуль, не выполняется.
Кроме того, бывают случаи, когда вы хотите, чтобы тесты были частью модуля, но не частью текста справки, что требует, чтобы тесты не включались в строку документации. Doctest ищет переменную уровня модуля с именем __test__
и использует ее для поиска других тестов. Если M.__test__
существует, то это должен быть dict, и каждая запись сопоставляет (строковое) имя объекту функции, объекту класса или строке. Выполняется поиск в строках документации функций и объектов класса, найденных в M.__test__
, и строки обрабатываются так, как если бы они были строками документации. В выходных данных отображается ключ K
в M.__test__
с именем M.__test__.K
.
Например, поместите этот блок кода в начало example.py
:
__test__ = {
'numbers': """
>>> factorial(6)
720
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
"""
}
Значение example.__test__["numbers"]
будет обрабатываться как строка документации, и все тесты внутри нее будут запущены. Важно отметить, что значение может быть сопоставлено функции, объекту класса или модулю; если это так, 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>
в вашем тестовом примере в каждом месте, где ожидается пустая строка.Все твердые символы табуляции заменяются пробелами с использованием 8-столбцовых разделителей табуляции. Табуляции в выходных данных, сгенерированных тестируемым кодом, не изменяются. Поскольку все жесткие табуляции в образце вывода расширены, это означает, что если вывод кода включает жесткие табуляции, то единственный способ, которым может быть выполнен тест, - это если используется параметр
NORMALIZE_WHITESPACE
или directive. В качестве альтернативы, тест можно переписать, чтобы получить выходные данные и сравнить их с ожидаемым значением в рамках теста. Такая обработка вкладок в исходном коде была разработана методом проб и ошибок и оказалась наименее подверженной ошибкам. Можно использовать другой алгоритм для обработки вкладок, написав пользовательский классDocTestParser
.Вывод в стандартный вывод записывается, но не выводится в stderr (обратные трассировки исключений записываются другим способом).
Если вы продолжаете строку с помощью обратной косой черты в интерактивном сеансе или по какой-либо другой причине используете обратную косую черту, вам следует использовать необработанную строку документации, которая сохранит ваши обратные косые черты точно в том виде, в каком вы их вводите:
>>> def f(x): ... r'''Backslashes in a raw docstring: m\n''' >>> print(f.__doc__) Backslashes in a raw docstring: m\n
В противном случае обратная косая черта будет интерпретироваться как часть строки. Например,
\n
, указанный выше, будет интерпретироваться как символ новой строки. В качестве альтернативы, вы можете удвоить каждую обратную косую черту в тестовой версии (и не использовать необработанную строку).:>>> 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
, а также имена, определенные ранее в запускаемой строке документации. В примерах не отображаются имена, определенные в других строках документации.
Вы можете принудительно использовать свой собственный dict в качестве контекста выполнения, передав вместо globs=your_dict
значение testmod()
или testfile()
.
А Как насчет Исключений¶
Нет проблем, при условии, что обратная трассировка является единственным результатом, полученным в примере: просто вставьте обратную трассировку. [1] Поскольку данные обратной трассировки содержат сведения, которые могут быстро меняться (например, точные пути к файлам и номера строк), это один из случаев, когда doctest прилагает все усилия, чтобы быть гибким в том, что он принимает.
Простой пример:
>>> [1, 2, 3].remove(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
Этот тест завершается успешно, если задано значение ValueError
с указанием list.remove(x): x not in list
, как показано на рисунке.
Ожидаемый вывод для исключения должен начинаться с заголовка обратной трассировки, который может быть любой из следующих двух строк с таким же отступом, как и в первой строке примера:
Traceback (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
Обратите внимание, что обратные трассировки обрабатываются особым образом. В частности, в переписанном примере использование ...
не зависит от параметра doctests ELLIPSIS
. Многоточие в этом примере можно было бы опустить, а можно было бы просто поставить три (или триста) запятых, или цифры, или текст пародии на Монти Пайтона с отступом.
Некоторые детали вам следует прочитать один раз, но запоминать их не нужно:
Doctest не может угадать, были ли ваши ожидаемые результаты получены в результате обратной трассировки исключения или в результате обычной печати. Так, например, пример, который ожидает, что
ValueError: 42 is prime
будет передан независимо от того, действительно лиValueError
поднят или если в примере просто выводится этот текст обратной трассировки. На практике обычный вывод редко начинается со строки заголовка обратной трассировки, так что реальных проблем это не создает.Каждая строка стека трассировки (если она присутствует) должна быть с отступом больше, чем в первой строке примера, * или* начинаться с не буквенно-цифрового символа. Первая строка, следующая за заголовком обратной трассировки, имеет тот же отступ и начинается с буквенно-цифрового символа, который считается началом описания исключения. Конечно, это правильно для подлинной обратной трассировки.
Если указан параметр
IGNORE_EXCEPTION_DETAIL
doctest, все, что следует за крайним левым двоеточием, и любая информация о модуле в имени исключения игнорируются.Интерактивная оболочка пропускает строку заголовка traceback для некоторых
SyntaxError
s. Но doctest использует строку заголовка traceback, чтобы отличать исключения от неисключений. Таким образом, в том редком случае, когда вам нужно протестироватьSyntaxError
, в котором отсутствует заголовок обратной трассировки, вам нужно будет вручную добавить строку заголовка обратной трассировки в ваш тестовый пример.
За некоторыми исключениями Python отображает местоположение ошибки с помощью
^
маркеров и тильд:>>> 1 + None File "<stdin>", line 1 1 + None ~~^~~~~~ TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
Поскольку строки, показывающие местоположение ошибки, располагаются перед типом исключения и его подробностями, они не проверяются doctest. Например, следующий тест прошел бы успешно, даже если бы он поместил маркер
^
в неправильное место:>>> 1 + None File "<stdin>", line 1 1 + None ^~~~~~~~ TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
Флажки опций¶
Ряд флагов параметров управляет различными аспектами поведения doctests. Символьные имена для флагов предоставляются в виде констант модуля, которые могут быть bitwise ORed вместе и передаваться различным функциям. Имена также могут быть использованы в doctest directives и могут быть переданы в интерфейс командной строки doctest с помощью опции -o
.
Добавлено в версии 3.4: Параметр командной строки -o
.
Первая группа параметров определяет семантику теста, контролируя аспекты того, как doctest решает, соответствует ли фактический результат ожидаемому результату примера:
- doctest.DONT_ACCEPT_TRUE_FOR_1¶
По умолчанию, если ожидаемый выходной блок содержит только
1
, фактический выходной блок, содержащий только1
или толькоTrue
, считается совпадающим, и аналогично для0
по сравнению сFalse
. Если указано значениеDONT_ACCEPT_TRUE_FOR_1
, ни одна из подстановок не допускается. Поведение по умолчанию соответствует тому, что Python изменил тип возвращаемого значения многих функций с integer на boolean; тесты, ожидающие вывода «маленького целого», по-прежнему работают в этих случаях. Этот вариант, вероятно, исчезнет, но не раньше, чем через несколько лет.
- doctest.DONT_ACCEPT_BLANKLINE¶
По умолчанию, если ожидаемый блок вывода содержит строку, содержащую только строку
<BLANKLINE>
, то эта строка будет соответствовать пустой строке в фактическом выводе. Поскольку ожидаемый вывод ограничен по-настоящему пустой строкой, это единственный способ сообщить, что ожидается пустая строка. Если указано значениеDONT_ACCEPT_BLANKLINE
, эта замена недопустима.
- doctest.NORMALIZE_WHITESPACE¶
Если указано, все последовательности пробелов (пробелы и символы новой строки) считаются равными. Любая последовательность пробелов в ожидаемом выводе будет соответствовать любой последовательности пробелов в фактическом выводе. По умолчанию пробелы должны совпадать в точности.
NORMALIZE_WHITESPACE
особенно полезно, когда строка ожидаемого результата очень длинная, и вы хотите перенести ее в несколько строк исходного кода.
- doctest.ELLIPSIS¶
Если указано, маркер с многоточием (
...
) в ожидаемом выводе может соответствовать любой подстроке в фактическом выводе. Это включает подстроки, которые охватывают границы строки, и пустые подстроки, поэтому лучше не усложнять использование этого параметра. Сложное использование может привести к тем же неожиданностям типа «ой, слишком много совпадений!», что и.*
в регулярных выражениях.
- doctest.IGNORE_EXCEPTION_DETAIL¶
Если указано, тесты, ожидающие исключения, выполняются до тех пор, пока возникает исключение ожидаемого типа, даже если данные (сообщение и полное имя исключения) не совпадают.
Например, пример, ожидающий
ValueError: 42
, пройдет, если фактическое вызванное исключение равноValueError: 3*14
, но завершится ошибкой, если, скажем, вместо этого будет вызваноTypeError
. Он также будет игнорировать любое полное имя, указанное перед классом exception, которое может варьироваться в зависимости от реализаций и версий 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 служат как документацией, так и тестовыми примерами, и пример должен быть включен в целях документирования, но не должен проверяться. Например, выходные данные примера могут быть случайными; или пример может зависеть от ресурсов, которые были бы недоступны тестировщику.
Флаг ПРОПУСКА также можно использовать для временного «комментирования» примеров.
- doctest.COMPARISON_FLAGS¶
Битовую маску или объедините все приведенные выше флаги сравнения.
Вторая группа опций управляет тем, как сообщаются сообщения о сбоях тестирования:
- doctest.REPORT_UDIFF¶
Если указано, сбои, связанные с многострочными ожидаемыми и фактическими выходными данными, отображаются с использованием унифицированного diff.
- doctest.REPORT_CDIFF¶
Если указано, сбои, связанные с многострочными ожидаемыми и фактическими выходными данными, будут отображаться с использованием контекстных различий.
- doctest.REPORT_NDIFF¶
Если указано, различия вычисляются с помощью
difflib.Differ
, используя тот же алгоритм, что и популярная утилитаndiff.py
. Это единственный метод, который отмечает различия как внутри строк, так и между ними. Например, если строка ожидаемого результата содержит цифру1
, в то время как фактический результат содержит буквуl
, вставляется строка с курсором, обозначающим несовпадающие позиции столбцов.
- doctest.REPORT_ONLY_FIRST_FAILURE¶
Если указано, отображайте первый неудачный пример в каждом тесте doctest, но отключайте вывод для всех остальных примеров. Это не позволит doctest сообщать о правильных примерах, которые не работают из-за предыдущих сбоев; но также может скрывать неправильные примеры, которые не работают независимо от первого сбоя. Если указано значение
REPORT_ONLY_FIRST_FAILURE
, остальные примеры по-прежнему выполняются и по-прежнему учитываются в общем количестве зарегистрированных сбоев; отключается только вывод.
- doctest.FAIL_FAST¶
Если указано, завершите работу после первого неудачного примера и не пытайтесь запускать остальные примеры. Таким образом, количество зарегистрированных сбоев будет не более 1. Этот флажок может быть полезен при отладке, поскольку примеры после первого сбоя даже не будут выдавать отладочный результат.
Командная строка doctest принимает параметр
-f
как сокращение от-o FAIL_FAST
.Добавлено в версии 3.4.
- doctest.REPORTING_FLAGS¶
Битовая маска или объединение всех вышеприведенных флагов отчетности.
Существует также способ зарегистрировать новые имена флагов опций, хотя это бесполезно, если только вы не собираетесь расширять doctest
внутренние компоненты с помощью подклассов:
- doctest.register_optionflag(name)¶
Создайте новый флаг параметра с заданным именем и верните целочисленное значение нового флага.
register_optionflag()
может использоваться при создании подклассовOutputChecker
илиDocTestRunner
для создания новых параметров, поддерживаемых вашими подклассами.register_optionflag()
всегда следует вызывать, используя следующую идиому:MY_FLAG = register_optionflag('MY_FLAG')
Директивы¶
Директивы Doctest могут использоваться для изменения option flags в отдельном примере. Директивы Doctest - это специальные комментарии Python, следующие за примером исходного кода.:
directive ::= "#" "doctest:"directive_options
directive_options ::=directive_option
(","directive_option
)* directive_option ::=on_or_off
directive_option_name
on_or_off ::= "+" | "-" directive_option_name ::= "DONT_ACCEPT_BLANKLINE" | "NORMALIZE_WHITESPACE" | ...
Пробелы не допускаются между +
или -
и именем параметра директивы. Именем параметра директивы может быть любое из названий флагов параметров, описанных выше.
Приведенные в примере директивы doctest изменяют поведение doctest для этого единственного примера. Используйте +
, чтобы включить указанное поведение, или -
, чтобы отключить его.
Например, этот тест проходит успешно:
>>> print(list(range(20))) # doctest: +NORMALIZE_WHITESPACE
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Без директивы это привело бы к сбою, как из-за того, что фактический вывод не содержит двух пробелов перед однозначными элементами списка, так и из-за того, что фактический вывод находится в одной строке. Этот тест также проходит, и для этого также требуется директива:
>>> print(list(range(20))) # doctest: +ELLIPSIS
[0, 1, ..., 18, 19]
В одной физической строке можно использовать несколько директив, разделенных запятыми:
>>> print(list(range(20))) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]
Если для одного примера используется несколько комментариев к директиве, то они объединяются:
>>> print(list(range(20))) # doctest: +ELLIPSIS
... # doctest: +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]
Как показано в предыдущем примере, вы можете добавить в свой пример строки ...
, содержащие только директивы. Это может быть полезно, когда пример слишком длинный, чтобы директива удобно помещалась в одной строке:
>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40)))
... # doctest: +ELLIPSIS
[0, ..., 4, 10, ..., 19, 30, ..., 39]
Обратите внимание, что, поскольку по умолчанию все параметры отключены, а директивы применяются только к тому примеру, в котором они указаны, включение параметров (через +
в директиве) обычно является единственным разумным выбором. Однако флаги параметров также могут быть переданы функциям, которые запускают doctests, устанавливая различные значения по умолчанию. В таких случаях может быть полезно отключить параметр с помощью -
в директиве.
Предупреждения¶
doctest
серьезно относится к требованию точного соответствия ожидаемых выходных данных. Если хотя бы один символ не совпадает, тест завершается неудачей. Возможно, это вас несколько раз удивит, поскольку вы узнаете, что именно делает Python и не гарантирует вывод. Например, при печати набора Python не гарантирует, что элемент будет напечатан в каком-либо определенном порядке, поэтому такой тест, как
>>> foo()
{"Hermione", "Harry"}
уязвим! Один из способов обойти это - сделать
>>> foo() == {"Hermione", "Harry"}
True
вместо. Другой способ заключается в том, чтобы сделать
>>> d = sorted(foo())
>>> d
['Harry', 'Hermione']
Есть и другие, но вы поняли идею.
Еще одна плохая идея - печатать файлы, содержащие адрес объекта, например
>>> id(1.0) # certain to fail some of the time
7948648
>>> class C: pass
>>> C() # the default repr() for instances embeds an address
<C object at 0x00AC18F0>
Директива ELLIPSIS
дает хороший подход к последнему примеру:
>>> C() # doctest: +ELLIPSIS
<C object at 0x...>
Числа с плавающей запятой также подвержены небольшим различиям в выводе на разных платформах, поскольку Python использует библиотеку платформы C для форматирования с плавающей запятой, а библиотеки C здесь сильно различаются по качеству.
>>> 1./7 # risky
0.14285714285714285
>>> print(1./7) # safer
0.142857142857
>>> print(round(1./7, 6)) # much safer
0.142857
Числа вида I/2.**J
безопасны на всех платформах, и я часто придумываю проверенные примеры для получения чисел такой формы:
>>> 3./4 # utterly safe
0.75
Простые дроби также легче усваиваются людьми, что способствует улучшению документации.
Базовый API¶
Функции testmod()
и testfile()
предоставляют простой интерфейс для doctest, которого должно быть достаточно для большинства базовых применений. Менее формальное введение в эти две функции приведено в разделах Простое использование: Проверка примеров в строках документации и Простое использование: Проверка примеров в текстовом файле.
- doctest.testfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, parser=DocTestParser(), encoding=None)¶
Все аргументы, кроме filename, являются необязательными и должны быть указаны в форме ключевого слова.
Проверьте примеры в файле с именем filename. Верните
(failure_count, test_count)
.Необязательный аргумент module_relative указывает, как следует интерпретировать имя файла:
Если значение module_relative равно
True
(по умолчанию), то filename указывает путь к модулю, не зависящий от операционной системы. По умолчанию этот путь относится к каталогу вызывающего модуля; но если указан аргумент package, то он относится к этому пакету. Чтобы обеспечить независимость от операционной системы, имя файла должно содержать символы/
для разделения сегментов пути и не может быть абсолютным путем (т.е. оно не может начинаться с/
).Если значение module_relative равно
False
, то filename указывает путь, зависящий от операционной системы. Путь может быть абсолютным или относительным; относительные пути разрешаются по отношению к текущему рабочему каталогу.
Необязательный аргумент name задает название теста; по умолчанию или если используется
None
,os.path.basename(filename)
.Необязательный аргумент package - это пакет Python или имя пакета Python, каталог которого должен использоваться в качестве базового каталога для имени файла, относящегося к модулю. Если пакет не указан, то в качестве базового каталога для имен файлов, относящихся к модулю, используется каталог вызывающего модуля. Указание package является ошибкой, если значение module_relative равно
False
.Необязательный аргумент globs задает dict, который будет использоваться в качестве глобальных параметров при выполнении примеров. Для тестирования doct создается новая неполная копия этого dict, поэтому примеры начинаются с чистого листа. По умолчанию, или если
None
, используется новый пустой dict.Необязательный аргумент extraglobs задает значение dict, объединенное с глобальными параметрами, используемыми для выполнения примеров. Это работает как
dict.update()
: если globs и extraglobs имеют общий ключ, соответствующее значение в extraglobs отображается в объединенном dict. По умолчанию, или еслиNone
, дополнительные глобальные параметры не используются. Это расширенная функция, которая позволяет параметризовать doctests. Например, doctest может быть написан для базового класса с использованием общего имени класса, а затем повторно использован для тестирования любого количества подклассов путем передачи extraglobs dict, сопоставляющего общее имя тестируемому подклассу.Необязательный аргумент verbose выводит много информации, если true, и выводит только ошибки, если false; по умолчанию, или если
None
, значение true тогда и только тогда, когда'-v'
находится вsys.argv
.Необязательный аргумент report при значении true выводит сводку в конце, в противном случае в конце ничего не выводится. В режиме verbose сводка является подробной, в противном случае сводка будет очень краткой (фактически пустой, если все тесты пройдены).
Необязательный аргумент optionflags (значение по умолчанию 0) принимает значение bitwise OR из всех флагов параметров. Смотрите раздел Флажки опций.
Необязательный аргумент raise_on_error по умолчанию имеет значение false. Если значение true, то при первом сбое или неожиданном исключении в примере генерируется исключение. Это позволяет отлаживать ошибки после завершения работы. По умолчанию примеры продолжают выполняться.
Необязательный аргумент parser указывает
DocTestParser
(или подкласс), который должен использоваться для извлечения тестов из файлов. По умолчанию используется обычный синтаксический анализатор (т.е.,DocTestParser()
).Необязательный аргумент encoding указывает кодировку, которая должна использоваться для преобразования файла в unicode.
- 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__
.Также проверьте примеры, доступные из dict
m.__test__
, если он существует.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 может быть строкой, модулем, функцией или объектом класса.
Для контекста выполнения используется неполная копия аргумента dictionary globs.
Необязательный аргумент name используется в сообщениях о сбоях и по умолчанию имеет значение
"NoName"
.Если необязательный аргумент verbose имеет значение true, выходные данные генерируются даже при отсутствии сбоев. По умолчанию выходные данные генерируются только в случае сбоя в примере.
Необязательный аргумент compileflags задает набор флагов, которые должны использоваться компилятором Python при запуске примеров. По умолчанию или если
None
, выводятся флаги, соответствующие набору будущих функций, найденных в globs.Необязательный аргумент optionflags работает так же, как и для функции
testfile()
, описанной выше.
Unittest API -интерфейс Unittest API¶
По мере роста вашей коллекции проверенных модулей вам понадобится способ систематического выполнения всех их тестов. doctest
предоставляет две функции, которые можно использовать для создания unittest
тестовых наборов из модулей и текстовых файлов, содержащих тесты. Чтобы интегрироваться с unittest
test discovery, включите функцию 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
из текстовых файлов и модулей с тестами doctests:
- 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 framework и запускать интерактивные примеры в каждом файле. Если пример в каком-либо файле не выполняется, то синтезированный модульный тест завершается неудачей, и возникает исключениеfailureException
, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.Передайте один или несколько путей (в виде строк) к текстовым файлам, подлежащим проверке.
Параметры могут быть предоставлены в качестве аргументов ключевых слов:
Необязательный аргумент module_relative указывает, как следует интерпретировать имена файлов в путях:
Если значение module_relative равно
True
(по умолчанию), то каждое имя файла в paths указывает независимый от операционной системы относительный путь к модулю. По умолчанию этот путь относится к каталогу вызывающего модуля; но если указан аргумент package, то он относится к этому пакету. Чтобы обеспечить независимость от операционной системы, каждое имя файла должно содержать символы/
для разделения сегментов пути и не может быть абсолютным путем (т.е. оно не может начинаться с/
).Если значение module_relative равно
False
, то каждое имя файла в paths указывает путь, зависящий от операционной системы. Путь может быть абсолютным или относительным; относительные пути разрешаются по отношению к текущему рабочему каталогу.
Необязательный аргумент package - это пакет Python или имя пакета Python, каталог которого следует использовать в качестве базового каталога для относящихся к модулю имен файлов в paths. Если пакет не указан, то каталог вызывающего модуля используется в качестве базового каталога для имен файлов, относящихся к модулю. Ошибка при указании пакета, если значение module_relative равно
False
.Необязательный аргумент setUp определяет функцию настройки для набора тестов. Она вызывается перед запуском тестов в каждом файле. Функции setUp будет передан объект
DocTest
. Функция настройки может получить доступ к глобальным параметрам теста как к атрибуту globs пройденного теста.Необязательный аргумент tearDown определяет функцию разборки для набора тестов. Она вызывается после выполнения тестов в каждом файле. Функции tearDown будет передан объект
DocTest
. Функция настройки может получить доступ к глобальным параметрам теста как к атрибуту globs пройденного теста.Необязательный аргумент globs - это словарь, содержащий исходные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию globs - это новый пустой словарь.
Необязательный аргумент optionflags определяет параметры проверки по умолчанию для тестов, которые создаются путем объединения отдельных флагов параметров. Смотрите раздел Флажки опций. Смотрите функцию
set_unittest_reportflags()
ниже, чтобы узнать, как лучше настроить параметры отчетов.Необязательный аргумент parser указывает
DocTestParser
(или подкласс), который должен использоваться для извлечения тестов из файлов. По умолчанию используется обычный синтаксический анализатор (т.е.,DocTestParser()
).Необязательный аргумент encoding указывает кодировку, которая должна использоваться для преобразования файла в unicode.
Глобальный
__file__
добавляется к глобальным параметрам, предоставляемым для тестов, загружаемых из текстового файла с помощьюDocFileSuite()
.
- doctest.DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None)¶
Преобразуйте тестовые задания для модуля в
unittest.TestSuite
.Возвращаемый
unittest.TestSuite
должен запускаться платформой unittest framework и запускать каждый тест doctest в модуле. Если какой-либо из тестов не выполняется, то синтезированный модульный тест завершается ошибкой, и возникает исключениеfailureException
, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.Необязательный аргумент module указывает модуль, который необходимо протестировать. Это может быть объект модуля или имя модуля (возможно, с точкой). Если не указано, используется модуль, вызывающий эту функцию.
Необязательный аргумент globs - это словарь, содержащий исходные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию globs - это новый пустой словарь.
Необязательный аргумент extraglobs указывает дополнительный набор глобальных переменных, который объединяется в globs. По умолчанию дополнительные глобальные переменные не используются.
Необязательный аргумент test_finder - это объект
DocTestFinder
(или его дополнительная замена), который используется для извлечения тестов из модуля.Необязательные аргументы setUp, tearDown и optionflags такие же, как для функции
DocFileSuite()
, описанной выше.Эта функция использует тот же метод поиска, что и
testmod()
.Изменено в версии 3.5:
DocTestSuite()
возвращает пустойunittest.TestSuite
, если module не содержит строк документации, вместо того, чтобы вызыватьValueError
.
- exception doctest.failureException¶
Когда тесты doctest, которые были преобразованы в модульные тесты с помощью
DocFileSuite()
илиDocTestSuite()
, завершаются ошибкой, возникает это исключение, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.
В общем, DocTestSuite()
создает unittest.TestSuite
из doctest.DocTestCase
экземпляров, а DocTestCase
является подклассом unittest.TestCase
. DocTestCase
не является документирован здесь (это внутренняя деталь), но изучение его кода может ответить на вопросы о точных деталях интеграции unittest
.
Аналогично, DocFileSuite()
создает unittest.TestSuite
из doctest.DocFileCase
экземпляров, а DocFileCase
является подклассом DocTestCase
.
Таким образом, оба способа создания unittest.TestSuite
запускают экземпляры DocTestCase
. Это важно по одной простой причине: когда вы запускаете doctest
функции самостоятельно, вы можете напрямую управлять doctest
используемыми параметрами, передавая флаги параметров doctest
функциям. Однако, если вы пишете фреймворк unittest
, unittest
в конечном счете определяет, когда и как выполняются тесты. Автор фреймворка обычно хочет контролировать doctest
параметров отчетов (возможно, например, заданных параметрами командной строки), но нет способа передать параметры через unittest
в doctest
тестировщиков.
По этой причине doctest
также поддерживает понятие doctest
сообщающих флагов, специфичных для поддержки unittest
, с помощью этой функции:
- doctest.set_unittest_reportflags(flags)¶
Установите для использования флаги
doctest
отчетов.Аргумент flags принимает значение bitwise OR из флагов параметров. Смотрите раздел Флажки опций. Можно использовать только «флаги отчетов».
Это глобальная настройка модуля, и она влияет на все будущие тесты, выполняемые модулем
unittest
: методrunTest()
изDocTestCase
проверяет флаги параметров, указанные для тестового примера, когдаDocTestCase
экземпляр был создан. Если флаги отчетов не были указаны (что является типичным и ожидаемым случаем),doctest
“ sunittest
флаги отчетов являются bitwise ORed флагами параметров, а флаги параметров, дополненные таким образом, передаются вDocTestRunner
экземпляр, созданный для запуска теста doctest. Если при создании экземпляраDocTestCase
были указаны какие-либо флаги отчетов, то флаги отчетовdoctest
’sunittest
игнорируются.Функция возвращает значение
unittest
флагов отчетности, действовавших до вызова функции.
Расширенный API¶
Базовый API - это простая оболочка, предназначенная для упрощения использования doctest. Он достаточно гибкий и должен удовлетворять потребностям большинства пользователей; однако, если вам требуется более детальный контроль над тестированием или вы хотите расширить возможности doctest, вам следует использовать расширенный API.
Расширенный API основан на двух классах контейнеров, которые используются для хранения интерактивных примеров, извлеченных из тестовых примеров doctest:
Example
: один Python statement, связанный с ожидаемым результатом.DocTest
: наборExample
, обычно извлекаемый из одной строки документации или текстового файла.
Для поиска, синтаксического анализа, запуска и проверки тестовых примеров определены дополнительные классы обработки:
DocTestFinder
: Находит все строки документации в данном модуле и используетDocTestParser
для созданияDocTest
из каждой строки документации, содержащей интерактивные примеры.DocTestParser
: Создает объектDocTest
из строки (например, строки документации объекта).DocTestRunner
: Выполняет примеры в видеDocTest
и используетOutputChecker
для проверки их вывода.OutputChecker
: Сравнивает фактический результат из примера doctest с ожидаемым результатом и решает, совпадают ли они.
Взаимосвязи между этими классами обработки кратко представлены на следующей диаграмме:
list of:
+------+ +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+ | ^ +---------+ | ^ (printed)
| | | Example | | |
v | | ... | v |
DocTestParser | Example | OutputChecker
+---------+
Проверенные объекты¶
- class doctest.DocTest(examples, globs, name, filename, lineno, docstring)¶
Набор тестовых примеров, которые должны выполняться в одном пространстве имен. Аргументы конструктора используются для инициализации атрибутов с одинаковыми именами.
DocTest
определяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.- examples¶
Список
Example
объектов, кодирующих отдельные интерактивные примеры на Python, которые должны быть запущены в этом тесте.
- globs¶
Пространство имен (оно же globals), в котором должны выполняться примеры. Это словарь, отображающий имена в значения. Любые изменения в пространстве имен, внесенные в примерах (например, привязка новых переменных), будут отражены в
globs
после запуска теста.
- name¶
Строковое имя, идентифицирующее
DocTest
. Как правило, это имя объекта или файла, из которого был извлечен тест.
- filename¶
Имя файла, из которого был извлечен этот
DocTest
; илиNone
, если имя файла неизвестно, или еслиDocTest
не был извлечен из файла.
- lineno¶
Номер строки в пределах
filename
, с которого начинается этотDocTest
, илиNone
, если номер строки недоступен. Этот номер строки равен нулю по отношению к началу файла.
- docstring¶
Строка, из которой был извлечен тест, или
None
, если строка недоступна, или если тест не был извлечен из строки.
Примеры объектов¶
- class doctest.Example(source, want, exc_msg=None, lineno=0, indent=0, options=None)¶
Единственный интерактивный пример, состоящий из инструкции Python и ожидаемого результата ее выполнения. Аргументы конструктора используются для инициализации атрибутов с одинаковыми именами.
Example
определяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.- source¶
Строка, содержащая исходный код примеров. Этот исходный код состоит из одного оператора Python и всегда заканчивается новой строкой; конструктор добавляет новую строку при необходимости.
- want¶
Ожидаемый результат выполнения исходного кода примеров (либо из стандартного вывода, либо с помощью обратной трассировки в случае исключения).
want
заканчивается новой строкой, если только не ожидается никакого результата, и в этом случае это пустая строка. Конструктор добавляет новую строку, когда это необходимо.
- exc_msg¶
Сообщение об исключении, сгенерированное примером, если ожидается, что пример сгенерирует исключение; или
None
, если не ожидается, что оно сгенерирует исключение. Это сообщение об исключении сравнивается с возвращаемым значением параметраtraceback.format_exception_only()
.exc_msg
заканчивается новой строкой, если только это неNone
. При необходимости конструктор добавляет новую строку.
- lineno¶
Номер строки в строке, содержащей этот пример, с которого начинается пример. Этот номер строки равен нулю по отношению к началу содержащей его строки.
- indent¶
Пример отступа в содержащей его строке, т.е. количество пробелов, которые предшествуют первому приглашению в примере.
- options¶
Сопоставление словаря с флагами параметров в
True
илиFalse
, которое используется для переопределения параметров по умолчанию в этом примере. Все флажки параметров, не содержащиеся в этом словаре, оставляются в их значении по умолчанию (как указано вDocTestRunner
’s optionflags). По умолчанию параметры не заданы.
Объекты DocTestFinder¶
- class doctest.DocTestFinder(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)¶
Класс обработки, используемый для извлечения
DocTest
, относящихся к данному объекту, из его строки документации и строк документации содержащихся в нем объектов.DocTest
s могут быть извлечены из модулей, классов, функций, методов, статических методов, classmethods и свойств.Необязательный аргумент verbose может использоваться для отображения объектов, которые ищет программа finder. По умолчанию используется значение
False
(без вывода).Необязательный аргумент parser указывает объект
DocTestParser
(или заменяющий его), который используется для извлечения doctests из docstrings.Если необязательный аргумент recurse имеет значение false, то
DocTestFinder.find()
будет проверять только данный объект, а не какие-либо содержащиеся в нем объекты.Если необязательный аргумент exclude_empty имеет значение false, то
DocTestFinder.find()
будет включать тесты для объектов с пустыми строками документации.DocTestFinder
определяет следующий метод:- find(obj[, name][, module][, globs][, extraglobs])¶
Возвращает список
DocTest
, которые определены в строке документации obj или в любой из содержащихся в ней строк документации объектов.Необязательный аргумент name указывает имя объекта; это имя будет использоваться для создания имен для возвращаемых
DocTest
s. Если name не указано, то используетсяobj.__name__
.Необязательный параметр module - это модуль, содержащий данный объект. Если модуль не указан или равен
None
, то программа поиска тестов попытается автоматически определить правильный модуль. Используется модуль объекта.:В качестве пространства имен по умолчанию, если не указано значение globs.
Чтобы DocTestFinder не извлекал DocTests из объектов, импортированных из других модулей. (Объекты, содержащие модули, отличные от module, игнорируются.)
Чтобы найти имя файла, содержащего объект.
Чтобы помочь найти номер строки объекта в его файле.
Если значение module равно
False
, попытка найти модуль не будет предпринята. Это непонятно, но используется в основном при тестировании самого doctest: если module равенFalse
илиNone
, но не может быть найден автоматически, то все объекты считаются принадлежащими (несуществующему) модулю, поэтому все содержащиеся в нем объекты будет (рекурсивно) производиться поиск доктринальных тестов.Глобальные значения для каждого
DocTest
формируются путем объединения globs и extraglobs (привязки в extraglobs переопределяют привязки в globs). Для каждогоDocTest
создается новая неполная копия словаря globals. Если значение globs не указано, то по умолчанию используется значение модуля __dict__, если указано, или{}
в противном случае. Если параметр extraglobs не указан, то по умолчанию используется значение{}
.
Объекты DocTestParser¶
- class doctest.DocTestParser¶
Класс обработки, используемый для извлечения интерактивных примеров из строки и использования их для создания объекта
DocTest
.DocTestParser
определяет следующие методы:- get_doctest(string, globs, name, filename, lineno)¶
Извлеките все примеры doctest из данной строки и соберите их в объект
DocTest
.globs, name, filename и lineno являются атрибутами для нового объекта
DocTest
. Дополнительную информацию смотрите в документации поDocTest
.
- get_examples(string, name='<string>')¶
Извлеките все примеры doctest из данной строки и верните их в виде списка объектов
Example
. Номера строк основаны на 0. Необязательный аргумент name - это имя, идентифицирующее эту строку, и используется только для сообщений об ошибках.
- parse(string, name='<string>')¶
Разделите данную строку на примеры и промежуточный текст и верните их в виде списка чередующихся
Example
s и строк. Номера строк дляExample
s основаны на 0. Необязательный аргумент name - это имя, идентифицирующее эту строку, и используется только для сообщений об ошибках.
Объекты DocTestRunner¶
- class doctest.DocTestRunner(checker=None, verbose=None, optionflags=0)¶
Класс обработки, используемый для выполнения и проверки интерактивных примеров в
DocTest
.Сравнение ожидаемых и фактических результатов выполняется с помощью
OutputChecker
. Это сравнение может быть настроено с помощью нескольких флажков; дополнительную информацию смотрите в разделе Флажки опций. Если флажков опций недостаточно, то сравнение также можно настроить, передав конструктору подклассOutputChecker
.Отображением выходных данных тестируемого устройства можно управлять двумя способами. Во-первых, можно передать функцию вывода в
TestRunner.run()
; эта функция будет вызываться со строками, которые должны отображаться. По умолчанию используется значениеsys.stdout.write
. Если захвата выходных данных недостаточно, то вывод на экран также можно настроить, создав подкласс DocTestRunner и переопределив методыreport_start()
,report_success()
,report_unexpected_exception()
, иreport_failure()
.Необязательный аргумент ключевого слова checker указывает объект
OutputChecker
(или альтернативную замену), который следует использовать для сравнения ожидаемых результатов с фактическими результатами тестовых примеров.Необязательный аргумент ключевого слова verbose определяет степень детализации
DocTestRunner
. Если значение verbose равноTrue
, то информация о каждом примере выводится по мере его запуска. Если значение verbose равноFalse
, то выводятся только ошибки. Если значение verbose не указано илиNone
, то используется подробный вывод, если используется параметр командной строки-v
.Необязательный аргумент ключевого слова optionflags можно использовать для управления тем, как программа тестирования сравнивает ожидаемый результат с фактическим и как отображаются ошибки. Дополнительные сведения см. в разделе Флажки опций.
DocTestRunner
определяет следующие методы:- report_start(out, test, example)¶
Сообщите, что программа тестирования собирается обработать данный пример. Этот метод предназначен для того, чтобы позволить подклассам
DocTestRunner
настраивать свои выходные данные; его не следует вызывать напрямую.example - это пример, который должен быть обработан. test - это тест, содержащий пример*. out - это функция вывода, которая была передана в
DocTestRunner.run()
.
- report_success(out, test, example, got)¶
Сообщите, что данный пример выполнен успешно. Этот метод предназначен для того, чтобы позволить подклассам
DocTestRunner
настраивать свои выходные данные; его не следует вызывать напрямую.example - это пример, который должен быть обработан. got - это фактический результат из примера. test - это тест, содержащий example. out - это функция вывода, которая была передана в
DocTestRunner.run()
.
- report_failure(out, test, example, got)¶
Сообщите, что данный пример не удался. Этот метод предназначен для того, чтобы позволить подклассам
DocTestRunner
настраивать свои выходные данные; его не следует вызывать напрямую.example - это пример, который должен быть обработан. got - это фактический результат из примера. test - это тест, содержащий example. out - это функция вывода, которая была передана в
DocTestRunner.run()
.
- report_unexpected_exception(out, test, example, exc_info)¶
Сообщите, что в данном примере возникло непредвиденное исключение. Этот метод предназначен для того, чтобы позволить подклассам
DocTestRunner
настраивать свои выходные данные; его не следует вызывать напрямую.example - это пример, который должен быть обработан. exc_info - это кортеж, содержащий информацию о непредвиденном исключении (возвращаемую
sys.exc_info()
). test - это тест, содержащий example. out - это функция вывода, которая была передана вDocTestRunner.run()
.
- run(test, compileflags=None, out=None, clear_globs=True)¶
Запустите примеры в test (объект
DocTest
) и отобразите результаты, используя функцию записи out.Примеры выполняются в пространстве имен
test.globs
. Если значение clear_globs равно true (по умолчанию), то это пространство имен будет очищено после тестовых запусков, чтобы облегчить сборку мусора. Если вы хотите проверить пространство имен после завершения теста, используйте clear_globs=False.compileflags задает набор флагов, которые должны использоваться компилятором Python при запуске примеров. Если не указано, то по умолчанию будет использоваться набор флагов будущего импорта, которые применяются к globs.
Выходные данные каждого примера проверяются с помощью средства проверки выходных данных
DocTestRunner
, и результаты форматируются с помощью методовDocTestRunner.report_*()
.
- summarize(verbose=None)¶
Выведите сводную информацию обо всех тестовых примерах, которые были запущены этим DocTestRunner, и верните named tuple
TestResults(failed, attempted)
.Необязательный аргумент verbose определяет, насколько подробной будет сводка. Если степень детализации не указана, то используется степень детализации
DocTestRunner
.
Объекты средства проверки вывода¶
- class doctest.OutputChecker¶
Класс, используемый для проверки того, соответствует ли фактический результат из примера doctest ожидаемому результату.
OutputChecker
определяет два метода:check_output()
, который сравнивает заданную пару выходных данных и возвращаетTrue
, если они совпадают; иoutput_difference()
, который возвращает строку, описывающую различия между двумя выходными данными.OutputChecker
определяет следующие методы:- check_output(want, got, optionflags)¶
Возвращает
True
, если фактический результат из примера (получено) соответствует ожидаемому результату (хочу). Эти строки всегда считаются совпадающими, если они идентичны; но в зависимости от того, какие флажки параметров использует тестировщик, также возможно несколько типов неточного совпадения. Дополнительную информацию о флажках параметров смотрите в разделе Флажки опций.
- output_difference(example, got, optionflags)¶
Возвращает строку, описывающую различия между ожидаемым результатом для данного примера (example) и фактическим результатом (got). optionflags - это набор флагов параметров, используемых для сравнения want и got.
Отладка¶
Doctest предоставляет несколько механизмов для отладки примеров doctest:
Несколько функций преобразуют doctests в исполняемые программы на Python, которые можно запускать с помощью отладчика Python,
pdb
.Класс
DebugRunner
является подклассомDocTestRunner
, который генерирует исключение для первого неудачного примера, содержащее информацию об этом примере. Эта информация может быть использована для выполнения посмертной отладки примера.Обращения
unittest
, сгенерированные с помощьюDocTestSuite()
, поддерживают методdebug()
, определенный с помощьюunittest.TestCase
.Вы можете добавить вызов
pdb.set_trace()
в тестовом примере, и вы попадете в отладчик Python, когда эта строка будет выполнена. Затем вы сможете проверять текущие значения переменных и так далее. Например, предположим, чтоa.py
содержит только эту строку документации модуля:""" >>> def f(x): ... g(x*2) >>> def g(x): ... print(x+3) ... import pdb; pdb.set_trace() >>> f(3) 9 """
Тогда интерактивный сеанс Python может выглядеть следующим образом:
>>> import a, doctest >>> doctest.testmod(a) --Return-- > <doctest a[1]>(3)g()->None -> import pdb; pdb.set_trace() (Pdb) list 1 def g(x): 2 print(x+3) 3 -> import pdb; pdb.set_trace() [EOF] (Pdb) p x 6 (Pdb) step --Return-- > <doctest a[0]>(2)f()->None -> g(x*2) (Pdb) list 1 def f(x): 2 -> g(x*2) [EOF] (Pdb) p x 3 (Pdb) step --Return-- > <doctest a[2]>(1)?()->None -> f(3) (Pdb) cont (0, 3) >>>
Функции, которые преобразуют doctests в код Python и, возможно, запускают синтезированный код в отладчике:
- doctest.script_from_examples(s)¶
Преобразуйте текст с примерами в скрипт.
Аргумент s - это строка, содержащая примеры проверки доктрины. Строка преобразуется в скрипт на Python, где примеры проверки доктрины в 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 для строки документации именованного объекта записывается во временный файл, а затем этот файл запускается под управлением отладчика Python,pdb
.Неполная копия
module.__dict__
используется как для локального, так и для глобального контекста выполнения.Необязательный аргумент pm определяет, будет ли использоваться посмертная отладка. Если pm имеет значение true, файл сценария запускается напрямую, и отладчик подключается только в том случае, если сценарий завершается в результате возникновения необработанного исключения. Если это происходит, то вызывается посмертная отладка через
pdb.post_mortem()
, передавая объект обратной трассировки из необработанного исключения. Если pm не указано или имеет значение false, скрипт запускается в отладчике с самого начала, путем передачи соответствующего вызоваexec()
вpdb.run()
.
- doctest.debug_src(src, pm=False, globs=None)¶
Отладьте doctests в виде строки.
Это похоже на функцию
debug()
, описанную выше, за исключением того, что строка, содержащая тестовые примеры, задается напрямую, с помощью аргумента src.Необязательный аргумент pm имеет то же значение, что и в функции
debug()
выше.Необязательный аргумент globs задает словарь для использования как в локальном, так и в глобальном контексте выполнения. Если он не указан или
None
, используется пустой словарь. Если указан, используется неполная копия словаря.
Класс DebugRunner
и особые исключения, которые он может вызывать, представляют наибольший интерес для авторов фреймворков тестирования и будут описаны здесь лишь в общих чертах. Смотрите исходный код, и особенно строку документации DebugRunner
(которая является тестом!) для получения более подробной информации:
- class doctest.DebugRunner(checker=None, verbose=None, optionflags=0)¶
Подкласс
DocTestRunner
, который генерирует исключение при обнаружении сбоя. Если возникает непредвиденное исключение, генерируется исключениеUnexpectedException
, содержащее тест, пример и исходное исключение. Если выходные данные не совпадают, то возникает исключениеDocTestFailure
, содержащее тест, пример и фактические выходные данные.Для получения информации о параметрах и методах конструктора смотрите документацию для
DocTestRunner
в разделе Расширенный API.
Есть два исключения, которые могут быть вызваны экземплярами DebugRunner
:
- exception doctest.DocTestFailure(test, example, got)¶
Исключение, вызванное
DocTestRunner
, сигнализирует о том, что фактический результат doctest examples не соответствует ожидаемому результату. Аргументы конструктора используются для инициализации атрибутов с одинаковыми именами.
DocTestFailure
определяет следующие атрибуты:
- DocTestFailure.got¶
Примеры фактических результатов.
- exception doctest.UnexpectedException(test, example, exc_info)¶
Исключение, вызванное
DocTestRunner
, сигнализирует о том, что в примере doctest возникло непредвиденное исключение. Аргументы конструктора используются для инициализации атрибутов с одинаковыми именами.
UnexpectedException
определяет следующие атрибуты:
- UnexpectedException.exc_info¶
Кортеж, содержащий информацию о неожиданном исключении, возвращаемую
sys.exc_info()
.
Коробка для мыла¶
Как упоминалось во введении, doctest
имеет три основных применения:
Проверка примеров в строках документации.
Регрессионное тестирование.
Грамотное тестирование исполняемой документации.
К этим видам использования предъявляются разные требования, и важно их различать. В частности, заполнение строк документации непонятными тестовыми примерами приводит к некачественной документации.
При написании docstring внимательно выбирайте примеры из docstring. Это искусство, которому нужно научиться - поначалу оно может показаться неестественным. Примеры должны придавать документации подлинную ценность. Хороший пример часто стоит многих слов. Если все будет сделано с осторожностью, примеры будут бесценны для ваших пользователей и многократно окупят время, затраченное на их сбор, по мере того как пройдут годы и ситуация изменится. Я до сих пор поражаюсь, как часто один из моих примеров doctest
перестает работать после «безобидного» изменения.
Doctest также является отличным инструментом для регрессионного тестирования, особенно если вы не экономите на пояснительном тексте. Благодаря чередованию прозы и примеров становится намного проще отслеживать, что на самом деле тестируется и почему. Когда тест завершается неудачей, хорошая проза может значительно облегчить понимание того, в чем проблема и как ее следует устранить. Это правда, что при тестировании на основе кода вы могли бы написать подробные комментарии, но мало кто из программистов это делает. Многие обнаружили, что использование тестовых подходов вместо этого приводит к гораздо более четким тестам. Возможно, это просто потому, что doctest делает написание прозы немного проще, чем написание кода, в то время как написание комментариев в коде немного сложнее. Я думаю, что дело не только в этом: естественным подходом при написании теста на основе doctest является желание объяснить тонкости вашего программного обеспечения и проиллюстрировать их примерами. Это, в свою очередь, естественным образом приводит к созданию тестовых файлов, которые начинаются с самых простых функций и логически переходят к усложнениям и крайним случаям. Результатом является связное изложение, а не набор изолированных функций, которые тестируют отдельные фрагменты функциональности, казалось бы, случайным образом. Это другой подход и приводит к другим результатам, стирая грань между тестированием и объяснением.
Регрессионное тестирование лучше всего проводить с выделенными объектами или файлами. Существует несколько вариантов организации тестов:
Напишите текстовые файлы, содержащие тестовые примеры в качестве интерактивных примеров, и протестируйте файлы, используя
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))
Сноски