Содержание страницы
26.2. doctest – Тестирование интерактивных примеров Python¶doctest – Test interactive Python examples
Модуль doctest ищет фрагменты текста, похожие на интерактивные сеансы Python, а затем выполняет эти сеансы, чтобы проверить, что они работают в точности так, как показано. Существует несколько распространённых способов использования doctest:
- Чтобы проверить, что докстринги модуля актуальны, убедившись, что все интерактивные примеры по-прежнему работают как описано.
- Чтобы выполнить регрессионное тестирование, проверяя, что интерактивные примеры из тестового файла или тестового объекта работают как ожидается.
- Чтобы написать учебную документацию для пакета, обильно иллюстрированную примерами ввода-вывода. В зависимости от того, на чём делается акцент – на примерах или на пояснительном тексте, это напоминает «грамотное тестирование» (literate testing) или «исполняемую документацию» (executable documentation).
Вот полный, но небольшой пример модуля:
"""
Это модуль "example".
Модуль example предоставляет одну функцию, 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 (самый последний вызов последним):
...
ValueError: n должно быть >= 0
Факториалы чисел с плавающей точкой допустимы, но число должно быть точным целым:
>>> factorial(30.1)
Traceback (самый последний вызов последним):
...
ValueError: n должно быть целым числом
>>> factorial(30.0)
265252859812191058636308480000000
Оно также не должно быть нелепо большим:
>>> factorial(1e100)
Traceback (самый последний вызов последним):
...
OverflowError: n слишком велико
"""
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! Приступайте. В следующих разделах приведены полные подробности. Обратите внимание, что в стандартном наборе тестов и библиотеках Python много примеров doctest. Особенно полезные примеры можно найти в стандартном тестовом файле Lib/test/test_doctest.py.
26.2.1. Простое использование: проверка примеров в строках документации¶Simple Usage: Checking Examples in Docstrings
Самый простой способ начать использовать 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() см. в разделе Basic API.
26.2.2. Простое использование: проверка примеров в текстовом файле¶Simple Usage: Checking Examples in a Text File
Ещё одно простое применение 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() не будет ничего выводить, если пример не завершится сбоем. Если же пример завершается сбоем, то неудачные примеры и причины сбоев выводятся в stdout в том же формате, что и в testmod().
По умолчанию testfile() ищет файлы в каталоге вызывающего модуля. Описание необязательных аргументов, которые можно использовать, чтобы указать ему искать файлы в других местах, см. в разделе Basic API.
Как и у testmod(), степень подробности testfile() можно задать с помощью флага командной строки -v или необязательного ключевого аргумента verbose.
Существует также сокращение в командной строке для запуска testfile(). Можно указать интерпретатору Python запустить модуль doctest непосредственно из стандартной библиотеки и передать имена файлов в командной строке:
python -m doctest -v example.txt
Поскольку имя файла не заканчивается на .py, doctest делает вывод, что его нужно запустить с помощью testfile(), а не testmod().
Дополнительную информацию о testfile() см. в разделе Basic API.
26.2.3. Как это работает¶How It Works
В этом разделе подробно рассматривается, как работает doctest: какие docstrings он просматривает, как находит интерактивные примеры, какой контекст выполнения использует, как обрабатывает исключения и как опции могут управлять его поведением. Это информация, которую нужно знать для написания примеров doctest; сведения о непосредственном запуске doctest на этих примерах приведены в следующих разделах.
26.2.3.1. Какие строки документации проверяются?¶Which Docstrings Are Examined?
Проверяются docstring модуля, а также docstrings всех функций, классов и методов. Объекты, импортированные в модуль, не проверяются.
Кроме того, если M.__test__ существует и "истинно", это должен быть словарь, и каждая запись сопоставляет (строковое) имя с объектом функции, объектом класса или строкой. Строки документации объектов функций и классов, найденные через M.__test__, ищутся, а строки обрабатываются так, как если бы они были строками документации. В выводе ключ K в M.__test__ появляется с именем
<name of M>.__test__.K
Любые найденные классы аналогично рекурсивно проверяются на наличие docstrings в содержащихся в них методах и вложенных классах.
26.2.3.2. Как распознаются примеры в строках документации?¶How are Docstring Examples Recognized?
В большинстве случаев копирование и вставка сессии интерактивной консоли работает нормально, но doctest не пытается точно эмулировать какой-либо конкретный Python shell.
>>> # комментарии игнорируются
>>> 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 (трассировки исключений перехватываются другим способом).
Если вы продолжаете строку обратной косой чертой в интерактивной сессии или по любой другой причине используете обратную косую черту, следует использовать сырой docstring, который сохранит обратные косые черты точно в том виде, как вы их ввели:
>>> def f(x): ... r'''Обратная косая черта в сырой докстринге: m\n''' >>> print(f.__doc__) Backslashes in a raw docstring: m\n
В противном случае обратная косая черта будет интерпретироваться как часть строки. Например, \n выше будет интерпретироваться как символ новой строки. В качестве альтернативы можно удвоить каждую обратную косую черту в версии для doctest (и не использовать сырую строку):
>>> def f(x): ... '''Обратная косая черта в сырой докстринге: m\\n''' >>> print(f.__doc__) Backslashes in a raw docstring: m\n
Начальный столбец не имеет значения:
>>> assert "Easy!" >>> import math >>> math.floor(1.9) 1
а также из ожидаемого вывода удаляется столько начальных пробельных символов, сколько их было в исходной строке '>>> ', с которой начинался пример.
26.2.3.3. Что такое контекст выполнения?¶What’s the Execution Context?
По умолчанию каждый раз, когда doctest находит докстринг для тестирования, он использует поверхностную копию глобальных переменных M, чтобы запуск тестов не изменял реальные глобальные переменные модуля, и чтобы один тест в M не мог оставить следы, которые случайно позволили бы другому тесту сработать. Это означает, что примеры могут свободно использовать любые имена, определённые на верхнем уровне в M, и имена, определённые ранее в выполняемом докстринге. Примеры не могут видеть имена, определённые в других докстрингах.
Можно принудительно использовать собственный словарь в качестве контекста выполнения, передав globs=your_dict в testmod() или testfile().
26.2.3.4. А что насчёт исключений?¶What About Exceptions?
Никаких проблем, при условии, что трассировка – единственный вывод, выдаваемый примером: просто вставьте трассировку. [1] Поскольку трассировки содержат детали, которые могут быстро меняться (например, точные пути к файлам и номера строк), это тот случай, когда doctest старается быть гибким в том, что он принимает.
Простой пример:
>>> [1, 2, 3].remove(42)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: list.remove(x): x not in list
Этот doctest завершается успешно, если возбуждается ValueError, с сообщением list.remove(x): x not in list, как показано.
Ожидаемый вывод для исключения должен начинаться с заголовка трассировки, которым может быть одна из следующих двух строк, с отступом, совпадающим с первой строкой примера:
Traceback (most recent call last):
Traceback (innermost last):
За заголовком трассировки следует необязательный стек трассировки, содержимое которого игнорируется doctest. Стек трассировки обычно опускается или копируется дословно из интерактивного сеанса.
За стеком трассировки следует самая интересная часть: строка(и), содержащие тип исключения и его описание. Обычно это последняя строка трассировки, но может занимать несколько строк, если у исключения многострочное описание:
>>> raise ValueError('multi\n line\ndetail')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: multi
line
detail
Последние три строки (начиная с ValueError) сравниваются с типом и сообщением исключения, а остальные игнорируются.
Лучшая практика – опускать стек трассировки, если он не добавляет существенной документационной ценности примеру. Так что последний пример, вероятно, лучше оформить так:
>>> raise ValueError('multi\n line\ndetail')
Traceback (most recent call last):
...
ValueError: multi
line
detail
Обратите внимание, что трассировки обрабатываются особым образом. В частности, в переписанном примере использование ... не зависит от опции ELLIPSIS модуля doctest. Многоточие в этом примере можно опустить или заменить тремя (или тремястами) запятыми, цифрами или даже отформатированной стенограммой скетча Монти Пайтона.
Некоторые детали, которые стоит прочитать один раз, но запоминать не обязательно:
Doctest не может угадать, взялся ли ожидаемый вывод из трассировки исключения или из обычной печати. Так, например, пример, ожидающий ValueError: 42 является простым числом будет пройден, независимо от того, действительно ли возбуждено ValueError или пример просто печатает этот текст трассировки. На практике обычный вывод редко начинается со строки заголовка трассировки, так что это не создаёт реальных проблем.
Каждая строка стека трассировки (если присутствует) должна иметь отступ больше, чем первая строка примера, или начинаться с небуквенно-цифрового символа. Первая строка после заголовка трассировки с таким же отступом и начинающаяся с буквенно-цифрового символа считается началом описания исключения. Конечно, это корректно работает для настоящих трассировок.
Если указана опция doctest IGNORE_EXCEPTION_DETAIL, то игнорируется всё, что следует за самой левой двоеточием, а также любая информация о модуле в имени исключения.
Интерактивная оболочка опускает строку заголовка трассировки для некоторых SyntaxError. Но doctest использует строку заголовка трассировки, чтобы отличать исключения от не-исключений. Поэтому в редком случае, когда нужно протестировать SyntaxError, в котором отсутствует заголовок трассировки, потребуется вручную добавить строку заголовка трассировки в тестовый пример.
Для некоторых SyntaxError Python отображает позицию символа синтаксической ошибки, используя маркер ^:
>>> 1 1 File "<stdin>", line 1 1 1 ^ SyntaxError: invalid syntaxПоскольку строки, показывающие положение ошибки, идут до типа и описания исключения, они не проверяются doctest. Например, следующий тест будет пройден, даже если ^-маркер поставлен в неверное место:
>>> 1 1 Traceback (most recent call last): File "<stdin>", line 1 1 1 ^ SyntaxError: invalid syntax
26.2.3.5. Флаги опций¶Option Flags
Несколько опциональных флагов управляют различными аспектами поведения doctest. Символические имена флагов предоставляются как константы модуля, которые можно объединять оператором OR и передавать различным функциям. Эти имена также можно использовать в директивах doctest.
Первая группа опций определяет семантику тестов, управляя аспектами того, как doctest решает, соответствует ли фактический вывод ожидаемому выводу примера:
- doctest.DONT_ACCEPT_TRUE_FOR_1¶
По умолчанию, если блок ожидаемого вывода содержит просто 1, блок фактического вывода, содержащий просто 1 или просто True, считается совпадением, и аналогично для 0 по сравнению с False. Когда указан DONT_ACCEPT_TRUE_FOR_1, ни одна из замен не допускается. Поведение по умолчанию учитывает, что Python изменил тип возвращаемого значения многих функций с целого числа на булев; doctests, ожидающие вывод «маленького целого», всё ещё работают в этих случаях. Эта опция, вероятно, исчезнет, но не в ближайшие годы.
- 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. Поэтому оба следующих варианта будут работать с указанным флагом независимо от того, выполняется ли тест под Python 2.7 или Python 3.2 (или более поздними версиями):
>>> 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 является также единственным понятным способом написать doctest, которому не важно сообщение исключения, но который продолжает проходить под Python 2.3 или более ранними версиями (эти выпуски не поддерживают директивы doctest и игнорируют их как нерелевантные комментарии). Например:
>>> (1, 2)[3] = 'moo' Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: object doesn't support item assignment
проходит в Python 2.3 и более поздних версиях Python с указанным флагом, хотя в Python 2.4 описание изменилось c «doesn’t» на «does not».
Изменено в версии 3.2: IGNORE_EXCEPTION_DETAIL теперь также игнорирует любую информацию, относящуюся к модулю, содержащему тестируемое исключение.
- doctest.SKIP¶
Если этот флаг указан, пример не выполняется вообще. Это может быть полезно в ситуациях,\nкогда доктесты служат одновременно документацией и тестовыми примерами, и пример\nдолжен быть включён для документации, но не должен проверяться. Например, вывод\nпримера может быть случайным, или пример может зависеть от ресурсов,\nнедоступных для тестового драйвера.
Флаг SKIP также можно использовать для временного «закомментирования» примеров.
- doctest.COMPARISON_FLAGS¶
Битовая маска, объединяющая все перечисленные выше флаги сравнения.
Вторая группа опций управляет тем, как сообщается о неудачных тестах:
- doctest.REPORT_UDIFF¶
Если указан, ошибки, включающие многострочные ожидаемый и фактический вывод, отображаются в виде unified diff.
- doctest.REPORT_CDIFF¶
Если указан, ошибки, включающие многострочные ожидаемый и фактический вывод, отображаются в виде context diff.
- doctest.REPORT_NDIFF¶
Если указано, различия вычисляются с помощью difflib.Differ, используя тот же алгоритм, что и популярная утилита ndiff.py. Это единственный метод, который отмечает различия как внутри строк, так и между строками. Например, если строка ожидаемого вывода содержит цифру 1, а фактический вывод содержит букву l, вставляется строка с символом «^», отмечающим несовпадающие позиции столбцов.
- doctest.REPORT_ONLY_FIRST_FAILURE¶
Если указано, отображается первый завершившийся с ошибкой пример в каждом doctest, но вывод для всех остальных примеров подавляется. Это предотвращает сообщение doctest о корректных примерах, которые стали неверными из-за предыдущих ошибок; однако это может также скрыть некорректные примеры, которые падают независимо от первой ошибки. Когда указано REPORT_ONLY_FIRST_FAILURE, остальные примеры по-прежнему выполняются и учитываются в общем числе сообщенных ошибок; подавляется только вывод.
- doctest.REPORTING_FLAGS¶
Битовая маска, объединяющая все перечисленные выше флаги отчёта.
Существует также способ регистрации новых имен опциональных флагов, хотя это имеет смысл только при расширении внутренней реализации doctest с помощью подклассов:
- doctest.register_optionflag(name)¶
Создает новый опциональный флаг с заданным именем и возвращает целочисленное значение нового флага. register_optionflag() можно использовать при создании подклассов OutputChecker или DocTestRunner для создания новых опций, поддерживаемых своими подклассами. register_optionflag() всегда следует вызывать, используя следующую идиому:
MY_FLAG = register_optionflag('MY_FLAG')
26.2.3.6. Директивы¶Directives
Директивы доктестов могут использоваться для изменения флагов опций для отдельного примера. Директивы доктестов – это\nспециальные комментарии 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" \| ...
Пробелы не допускаются между + или - и именем опции\nдирективы. Имя опции директивы может быть любым из имён флагов опций,\nописанных выше.
Директивы доктестов примера изменяют поведение doctest только для этого\nодного примера. Используйте + для включения указанного поведения или\n- для его отключения.
Например, этот тест проходит:
>>> 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]
Без директивы он бы не прошёл, и потому что в фактическом выводе нет\nдвух пробелов перед элементами списка из одной цифры, и потому что фактический вывод\nнаходится на одной строке. Этот тест тоже проходит и тоже требует директивы\nдля этого:
>>> 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]
Обратите внимание: поскольку все опции по умолчанию отключены, а директивы применяются только к тому примеру, в котором они находятся, включение опций (через + в директиве) обычно является единственным осмысленным выбором. Однако флаги опций также могут передаваться функциям, запускающим доктесты, устанавливая другие значения по умолчанию. В таких случаях отключение опции через - в директиве может быть полезным.
26.2.3.7. Предупреждения¶Warnings
doctest очень строг в отношении точного совпадения ожидаемого вывода. Если не совпадает хотя бы один символ, тест считается проваленным. В процессе изучения того, что именно Python гарантирует, а что нет в отношении вывода, это может несколько раз удивить. Например, при выводе словаря Python не гарантирует, что пары ключ-значение будут выведены в каком-то определенном порядке, поэтому такой тест
>>> foo()
{"Hermione": "hippogryph", "Harry": "broomstick"}
является уязвимым! Один из способов обойти это – сделать
>>> foo() == {"Hermione": "hippogryph", "Harry": "broomstick"}
True
вместо этого. Другой способ – сделать
>>> d = sorted(foo().items())
>>> d
[('Harry', 'broomstick'), ('Hermione', 'hippogryph')]
Есть и другие, но идея понятна.
Ещё одна плохая идея – печатать то, что содержит адрес объекта, например
>>> id(1.0) # обязательно иногда будет ошибаться
7948648
>>> class C: pass
>>> C() # стандартный repr() для экземпляров содержит адрес
<__main__.C instance at 0x00AC18F0>
Директива ELLIPSIS дает хороший подход для последнего примера:
>>> C() #doctest: +ELLIPSIS
<__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 безопасны на всех платформах, и примеры для doctest часто намеренно составляются так, чтобы получались числа такой формы:
>>> 3./4 # абсолютно безопасно
0.75
Простые дроби также легче понимаются людьми, что улучшает документацию.
26.2.4. Базовый API¶Basic API
Функции testmod() и testfile() предоставляют простой интерфейс к doctest, которого должно быть достаточно для большинства базовых применений. Для менее формального введения в эти две функции см. разделы Простое использование: проверка примеров в docstrings и Простое использование: проверка примеров в текстовом файле.
- doctest.testfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, parser=DocTestParser(), encoding=None)¶
Все аргументы, кроме filename, являются необязательными и должны указываться в форме ключевых слов.
Тестирует примеры в файле с именем filename. Возвращает (failure_count, test_count).
Необязательный аргумент module_relative определяет, как следует интерпретировать имя файла:
- Если module_relative равен True (по умолчанию), то filename задает независимый от ОС путь, относительный модуля. По умолчанию этот путь относителен каталога вызывающего модуля; но если указан аргумент package, то путь относителен этого пакета. Для обеспечения независимости от ОС в filename следует использовать символ / для разделения сегментов пути, и он не может быть абсолютным путем (т.е. не должен начинаться с /).
- Если module_relative равен False, то filename задает путь, зависящий от ОС. Путь может быть абсолютным или относительным; относительные пути разрешаются относительно текущего рабочего каталога.
Необязательный аргумент name задает имя теста; по умолчанию или если None, используется os.path.basename(filename).
Необязательный аргумент package – это пакет Python или имя пакета Python, чей каталог должен использоваться как базовый каталог для имени файла, относительного модуля. Если пакет не указан, то в качестве базового каталога для имен файлов, относительных модуля, используется каталог вызывающего модуля. Указывать package, если module_relative равно False, является ошибкой.
Необязательный аргумент globs задает словарь, используемый в качестве глобальных переменных при выполнении примеров. Для doctest создается новая поверхностная копия этого словаря, так что примеры начинают с чистого листа. По умолчанию или если None, используется новый пустой словарь.
Необязательный аргумент extraglobs задает словарь, объединяемый с глобальными переменными, используемыми для выполнения примеров. Это работает как dict.update(): если globs и extraglobs имеют общий ключ, соответствующее значение из extraglobs появляется в объединенном словаре. По умолчанию или если None, дополнительные глобальные переменные не используются. Это расширенная возможность, позволяющая параметризацию doctest. Например, doctest может быть написан для базового класса с использованием обобщенного имени класса, а затем повторно использован для тестирования любого количества подклассов путем передачи словаря extraglobs, отображающего обобщенное имя на тестируемый подкласс.
Необязательный аргумент verbose выводит много информации, если равен true, и только ошибки, если false; по умолчанию или если None, он равен true тогда и только тогда, когда '-v' присутствует в sys.argv.
Необязательный аргумент report: если истина, выводит сводку в конце, иначе ничего не выводит. В подробном режиме сводка подробная, в противном случае – очень краткая (фактически пустая, если все тесты пройдены).
Необязательный аргумент optionflags объединяет опциональные флаги с помощью 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, должны передаваться как именованные.
Тестирует примеры в docstrings функций и классов, доступных из модуля m (или модуля __main__, если m не указан или равен None), начиная с m.__doc__.
Также тестирует примеры, доступные из словаря m.__test__, если он существует и не равен None. m.__test__ отображает имена (строки) на функции, классы и строки; docstrings функций и классов проверяются на наличие примеров; строки проверяются напрямую, как если бы они были docstrings.
Проверяются только строки документации, прикреплённые к объектам, принадлежащим модулю m.
Возвращает (failure_count, test_count).
Необязательный аргумент name задает имя модуля; по умолчанию или если None, используется m.__name__.
Необязательный аргумент exclude_empty по умолчанию равен false. Если true, объекты, для которых не найдено ни одного doctest, исключаются из рассмотрения. Значение по умолчанию – это хак для обратной совместимости, чтобы код, все еще использующий doctest.master.summarize() вместе с testmod(), продолжал получать вывод для объектов без тестов. Аргумент exclude_empty конструктора более нового DocTestFinder по умолчанию равен true.
Необязательные аргументы extraglobs, verbose, report, optionflags, raise_on_error и globs аналогичны таковым для функции testfile() выше, за исключением того, что globs по умолчанию равен m.__dict__.
Существует также функция для запуска doctest-ов, связанных с одним объектом. Эта функция предоставляется для обратной совместимости. Ее не планируется объявлять устаревшей, но она редко бывает полезной:
- 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() выше.
26.2.5. API для unittest¶Unittest API
По мере роста вашей коллекции модулей с doctest, вам понадобится способ систематически запускать все их doctest. doctest предоставляет две функции, которые можно использовать для создания тестовых наборов unittest из модулей и текстовых файлов, содержащих doctest. Чтобы интегрироваться с обнаружением тестов unittest, включите функцию load_tests() в ваш тестовый модуль:
import unittest
import doctest
import my_module_with_doctests
def load_tests(loader, tests, ignore):
tests.addTests(doctest.DocTestSuite(my_module_with_doctests))
return tests
Существует две основные функции для создания экземпляров unittest.TestSuite на основе текстовых файлов и модулей с doctest:
- doctest.DocFileSuite(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None)¶
Преобразует тесты doctest из одного или нескольких текстовых файлов в unittest.TestSuite.
Возвращаемый unittest.TestSuite должен выполняться фреймворком unittest и запускает интерактивные примеры в каждом файле. Если пример в каком-либо файле не срабатывает, то созданный тест модуля завершается неудачей, и возникает исключение failureException с указанием имени файла, содержащего тест, и (иногда приблизительным) номером строки.
Передаётся один или несколько путей (в виде строк) к текстовым файлам для проверки.
Параметры могут быть указаны в виде именованных аргументов:
Необязательный аргумент module_relative определяет, как должны интерпретироваться имена файлов в paths:
- Если module_relative равно True (по умолчанию), то каждое имя файла в paths задаёт независимый от ОС путь относительно модуля. По умолчанию этот путь относителен к каталогу вызывающего модуля; но если указан аргумент package, то он относителен к этому пакету. Чтобы гарантировать независимость от ОС, каждое имя файла должно использовать символы / для разделения сегментов пути и не должно быть абсолютным путём (т.е. не должно начинаться с /).
- Если module_relative равно False, то каждое имя файла в paths задаёт путь, зависящий от ОС. Путь может быть абсолютным или относительным; относительные пути разрешаются относительно текущего рабочего каталога.
Необязательный аргумент package – это пакет Python или имя пакета Python, каталог которого должен использоваться как базовый каталог для имён файлов, относительных модуля, в paths. Если пакет не указан, то в качестве базового каталога для имён файлов, относительных модуля, используется каталог вызывающего модуля. Указывать package, если module_relative равно False, является ошибкой.
Необязательный аргумент setUp задаёт функцию настройки для тестового набора. Она вызывается перед запуском тестов в каждом файле. Функции setUp будет передан объект DocTest. Функция setUp может получить доступ к глобальным переменным теста через атрибут globs переданного теста.
Необязательный аргумент tearDown задаёт функцию завершения для тестового набора. Она вызывается после запуска тестов в каждом файле. Функции tearDown будет передан объект DocTest. Функция setUp может получить доступ к глобальным переменным теста через атрибут globs переданного теста.
Необязательный аргумент globs – это словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создаётся новая копия этого словаря. По умолчанию globs – новый пустой словарь.
Необязательный аргумент optionflags задаёт стандартные параметры doctest для тестов, создаваемые путём объединения отдельных флагов опций (логическим ИЛИ). См. раздел Option Flags. См. функцию set_unittest_reportflags() ниже для лучшего способа задания параметров отчёта.
Необязательный аргумент parser задаёт объект DocTestParser (или подкласс), который следует использовать для извлечения тестов из файлов. По умолчанию используется обычный анализатор (т.е. DocTestParser()).
Необязательный аргумент encoding задаёт кодировку, которая должна использоваться для преобразования файла в Unicode.
Глобальная переменная __file__ добавляется в область глобальных переменных, передаваемых doctest, загруженному из текстового файла с помощью DocFileSuite().
- doctest.DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None)¶
Преобразует тесты doctest для модуля в unittest.TestSuite.
Возвращаемый unittest.TestSuite должен выполняться фреймворком unittest и запускает каждый doctest в модуле. Если какой-либо из doctest завершится неудачей, то созданный модульный тест завершается неудачей, и возникает исключение failureException с указанием имени файла, содержащего тест, и (иногда приблизительным) номером строки.
Необязательный аргумент module задаёт тестируемый модуль. Это может быть объект модуля или (возможно, с точками) имя модуля. Если он не указан, используется модуль, из которого вызывается эта функция.
Необязательный аргумент globs – это словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создаётся новая копия этого словаря. По умолчанию globs – новый пустой словарь.
Необязательный аргумент extraglobs задаёт дополнительный набор глобальных переменных, который объединяется с globs. По умолчанию дополнительные глобальные переменные не используются.
Необязательный аргумент test_finder – это объект DocTestFinder (или его полноценная замена), который извлекает doctest из модуля.
Необязательные аргументы setUp, tearDown и optionflags аналогичны одноимённым аргументам функции DocFileSuite() выше.
Эта функция использует тот же метод поиска, что и testmod().
Примечание
В отличие от testmod() и DocTestFinder, эта функция вызывает ValueError, если module не содержит строк документации. Эту ошибку можно предотвратить, передав экземпляр DocTestFinder в качестве аргумента test_finder с его именованным аргументом exclude_empty, установленным в False:
>>> finder = doctest.DocTestFinder(exclude_empty=False) >>> suite = doctest.DocTestSuite(test_finder=finder)
Внутренне 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 объединяет опциональные флаги с помощью OR. Смотрите раздел Опциональные флаги. Можно использовать только «флаги отчетности».
Это глобальная настройка модуля, которая влияет на все будущие doctest, запускаемые модулем unittest: метод runTest() класса DocTestCase смотрит на флаги опций, указанные для тестового случая при создании экземпляра DocTestCase. Если флаги отчётности не были указаны (что является типичным и ожидаемым случаем), флаги отчётности doctest unittest объединяются по ИЛИ с флагами опций, и дополненные таким образом флаги опций передаются экземпляру DocTestRunner, созданному для выполнения doctest. Если при создании экземпляра DocTestCase были указаны какие-либо флаги отчётности, флаги отчётности doctest unittest игнорируются.
Функция возвращает значение флагов отчётности unittest, действовавших до её вызова.
26.2.6. Расширенный API¶Advanced API
Базовый API – это простая обёртка, предназначенная для упрощения использования doctest. Он достаточно гибок и должен удовлетворять потребности большинства пользователей; однако, если требуется более детальный контроль над тестированием или расширение возможностей doctest, следует использовать расширенный API.
Расширенный API строится вокруг двух классов-контейнеров, которые используются для хранения интерактивных примеров, извлечённых из тестовых сценариев doctest:
- Example: одна инструкция statement на языке Python в паре с ожидаемым выводом.
- DocTest: набор объектов Example, обычно извлекаемый из одной строки документации или текстового файла.
Для поиска, разбора, выполнения и проверки примеров doctest определены дополнительные классы обработки:
- DocTestFinder: находит все строки документации в заданном модуле и использует DocTestParser для создания DocTest из каждой строки документации, содержащей интерактивные примеры.
- DocTestParser: создаёт объект DocTest из строки (например, docstring объекта).
- DocTestRunner: выполняет примеры в DocTest и использует OutputChecker для проверки их вывода.
- OutputChecker: сравнивает фактический вывод из примера doctest с ожидаемым выводом и определяет, совпадают ли они.
Взаимосвязи между этими классами обработки показаны на следующей диаграмме.
list of:
+------+ +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+ | ^ +---------+ | ^ (printed)
| | | Example | | |
v | | ... | v |
DocTestParser | Example | OutputChecker
+---------+
26.2.6.1. Объекты DocTest¶DocTest Objects
- 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», если строка недоступна, или если тест не был извлечён из строки.
26.2.6.2. Объекты Example¶Example Objects
- 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‘s optionflags). По умолчанию опции не заданы.
26.2.6.3. Объекты DocTestFinder¶DocTestFinder objects
- class doctest.DocTestFinder(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)¶
Обрабатывающий класс, используемый для извлечения объектов DocTest, относящихся к заданному объекту, из его строки документации и строк документации вложенных в него объектов. DocTest могут быть извлечены из объектов следующих типов: модули, функции, классы, методы, статические методы, методы класса и свойства.
Необязательный аргумент verbose можно использовать для вывода объектов, которые ищет поисковик. По умолчанию False (вывод отсутствует).
Необязательный аргумент parser задаёт объект DocTestParser (или его полноценную замену), который используется для извлечения доктестов из строк документации.
Если необязательный аргумент recurse равен false, то DocTestFinder.find() будет проверять только переданный объект, но не содержащиеся в нём объекты.
Если необязательный аргумент exclude_empty равен false, то DocTestFinder.find() включит тесты для объектов с пустыми строками документации.
DocTestFinder определяет следующий метод:
- find(obj[, name][, module][, globs][, extraglobs])¶
Возвращает список объектов DocTest, определённых строкой документации obj или строками документации любых содержащихся в нём объектов.
Необязательный аргумент name задаёт имя объекта; это имя будет использоваться для формирования имён возвращаемых DocTest. Если name не указан, то используется obj.__name__.
Необязательный параметр module – это модуль, содержащий переданный объект. Если модуль не указан или равен None, то поисковик тестов попытается автоматически определить правильный модуль. Модуль объекта используется:
- В качестве пространства имён по умолчанию, если globs не указан.
- Чтобы предотвратить извлечение DocTests объектом DocTestFinder из объектов, импортированных из других модулей. (Содержащиеся объекты, чей модуль отличается от module, игнорируются.)
- Для поиска имени файла, содержащего объект.
- Для помощи в определении номера строки объекта в его файле.
Если module равен False, никаких попыток найти модуль не будет. Это неочевидное поведение, в основном используемое при тестировании самого doctest: если module равен False или None, но не может быть найден автоматически, то считается, что все объекты принадлежат (несуществующему) модулю, и поэтому все содержащиеся объекты будут (рекурсивно) проверены на наличие доктестов.
Глобальные переменные для каждого DocTest формируются путём объединения globs и extraglobs (привязки в extraglobs переопределяют привязки в globs). Для каждого DocTest создаётся новая поверхностная копия словаря глобальных переменных. Если globs не указан, то по умолчанию используется __dict__ модуля, если он указан, или {} в противном случае. Если extraglobs не указан, то по умолчанию {}.
26.2.6.4. Объекты DocTestParser¶DocTestParser objects
- class doctest.DocTestParser¶
Класс обработки, используемый для извлечения интерактивных примеров из строки и создания на их основе объекта DocTest.
DocTestParser определяет следующие методы:
- get_doctest(string, globs, name, filename, lineno)¶
Извлекает все примеры доктестов из заданной строки и собирает их в объект DocTest.
globs, name, filename и lineno являются атрибутами нового объекта DocTest. Дополнительные сведения см. в документации DocTest.
- get_examples(string, name='<string>')¶
Извлекает все примеры доктестов из заданной строки и возвращает их в виде списка объектов Example. Номера строк начинаются с 0. Необязательный аргумент name – это имя, идентифицирующее данную строку; используется только для сообщений об ошибках.
- parse(string, name='<string>')¶
Разделяет заданную строку на примеры и промежуточный текст и возвращает их в виде списка, чередующего объекты Example и строки. Номера строк для Example начинаются с 0. Необязательный аргумент name – это имя, идентифицирующее данную строку; используется только для сообщений об ошибках.
26.2.6.5. Объекты DocTestRunner¶DocTestRunner objects
- class doctest.DocTestRunner(checker=None, verbose=None, optionflags=0)¶
Класс обработки, используемый для выполнения и проверки интерактивных примеров в DocTest.
Сравнение ожидаемых и фактических результатов выполняется с помощью OutputChecker. Это сравнение можно настроить с помощью ряда флагов; дополнительную информацию см. в разделе Option Flags. Если флагов недостаточно, то сравнение также можно настроить, передав подкласс OutputChecker в конструктор.
Вывод тестового исполнителя можно управлять двумя способами. Во-первых, можно передать функцию вывода в TestRunner.run(); эта функция будет вызываться со строками, которые нужно отобразить. По умолчанию используется sys.stdout.write. Если захвата вывода недостаточно, то вывод также можно настроить, создав подкласс DocTestRunner и переопределив методы report_start(), report_success(), report_unexpected_exception() и report_failure().
Необязательный именованный аргумент checker задаёт объект OutputChecker (или его замену), который должен использоваться для сравнения ожидаемых результатов с фактическими результатами примеров doctest.
Необязательный именованный аргумент verbose управляет многословностью DocTestRunner. Если verbose равен True, то информация выводится о каждом примере по мере его выполнения. Если verbose равен False, то выводятся только неудачи. Если verbose не указан или равен None, то многословный вывод используется только тогда, когда используется флаг командной строки -v.
Необязательный именованный аргумент optionflags позволяет управлять тем, как исполнитель тестов сравнивает ожидаемый вывод с фактическим и как отображает ошибки. Дополнительную информацию см. в разделе Флаги опций.
DocTestParser определяет следующие методы:
- report_start(out, test, example)¶
Сообщает, что исполнитель тестов собирается обработать данный пример. Этот метод предоставляется, чтобы подклассы DocTestRunner могли настроить свой вывод; не следует вызывать его напрямую.
example – это пример, который будет обработан. test – это тест, содержащий пример. out – это функция вывода, переданная в DocTestRunner.run().
- report_success(out, test, example, got)¶
Сообщает, что данный пример выполнился успешно. Этот метод предоставляется, чтобы подклассы DocTestRunner могли настроить свой вывод; не следует вызывать его напрямую.
example – это пример, который будет обработан. got – это фактический вывод из примера. test – это тест, содержащий example. out – это функция вывода, переданная в DocTestRunner.run().
- report_failure(out, test, example, got)¶
Сообщает, что данный пример завершился неудачей. Этот метод предоставляется, чтобы подклассы DocTestRunner могли настроить свой вывод; не следует вызывать его напрямую.
example – это пример, который будет обработан. got – это фактический вывод из примера. test – это тест, содержащий example. out – это функция вывода, переданная в DocTestRunner.run().
- report_unexpected_exception(out, test, example, exc_info)¶
Сообщает, что данный пример вызвал неожиданное исключение. Этот метод предоставляется, чтобы подклассы DocTestRunner могли настроить свой вывод; не следует вызывать его напрямую.
example – это пример, который будет обработан. exc_info – это кортеж, содержащий информацию о неожиданном исключении (как возвращается sys.exc_info()). test – это тест, содержащий example. out – это функция вывода, переданная в DocTestRunner.run().
- run(test, compileflags=None, out=None, clear_globs=True)¶
Запускает примеры из test (объект DocTest) и отображает результаты с помощью функции записи out.
Примеры выполняются в пространстве имён test.globs. Если clear_globs равно истине (по умолчанию), то это пространство имён будет очищено после выполнения теста, чтобы помочь сборке мусора. Если требуется изучить пространство имён после завершения теста, используйте clear_globs=False.
compileflags задаёт набор флагов, которые должен использовать компилятор Python при выполнении примеров. Если не указан, по умолчанию используется набор флагов future-import, действующих для globs.
Вывод каждого примера проверяется с помощью средства проверки вывода DocTestRunner, а результаты форматируются методами DocTestRunner.report_*().
- summarize(verbose=None)¶
Выводит сводку всех тестовых случаев, выполненных данным DocTestRunner, и возвращает именованный кортеж TestResults(failed, attempted).
Необязательный аргумент verbose управляет детализацией сводки. Если многословность не указана, используется многословность DocTestRunner.
26.2.6.6. Объекты OutputChecker¶OutputChecker objects
- class doctest.OutputChecker¶
Класс, используемый для проверки, соответствует ли фактический вывод из примера doctest ожидаемому выводу. OutputChecker определяет два метода: check_output(), который сравнивает заданную пару выводов и возвращает истину, если они совпадают; и output_difference(), который возвращает строку, описывающую различия между двумя выводами.
OutputChecker определяет следующие методы:
- check_output(want, got, optionflags)¶
Возвращает True тогда и только тогда, когда фактический вывод из примера (got) совпадает с ожидаемым выводом (want). Эти строки всегда считаются совпадающими, если они идентичны; но в зависимости от того, какие флаги опций использует исполнитель тестов, возможны также несколько типов нестрогого совпадения. См. раздел Option Flags для получения дополнительной информации о флагах опций.
- output_difference(example, got, optionflags)¶
Возвращает строку с описанием различий между ожидаемым выводом для заданного примера (example) и фактическим выводом (got). optionflags – это набор флагов опций, используемых для сравнения want и got.
26.2.7. Отладка¶Debugging
Doctest предоставляет несколько механизмов для отладки примеров doctest:
Several functions convert doctests to executable Python programs, which can be run under the Python debugger, pdb.
Класс DebugRunner является подклассом DocTestRunner, который вызывает исключение при первом же несоответствии примера, содержащее информацию об этом примере. Эти сведения можно использовать для посмертной отладки примера.
Тестовые случаи unittest, созданные функцией DocTestSuite(), поддерживают метод debug(), определённый в unittest.TestCase.
Можно добавить вызов pdb.set_trace() в примере doctest; при выполнении этой строки произойдёт вход в отладчик Python. После этого можно просматривать текущие значения переменных и т.д. Например, пусть a.py содержит только следующую строку документации модуля:
""" >>> def f(x): ... g(x*2) >>> def g(x): ... print(x+3) ... import pdb; pdb.set_trace() >>> f(3) 9 """
Тогда интерактивный сеанс Python может выглядеть так:
>>> import a, doctest >>> doctest.testmod(a) --Return-- > <doctest a[1]>(3)g()->None -> import pdb; pdb.set_trace() (Pdb) list 1 def g(x): 2 print(x+3) 3 -> import pdb; pdb.set_trace() [EOF] (Pdb) p x 6 (Pdb) step --Return-- > <doctest a[0]>(2)f()->None -> g(x*2) (Pdb) list 1 def f(x): 2 -> g(x*2) [EOF] (Pdb) p x 3 (Pdb) step --Return-- > <doctest a[2]>(1)?()->None -> f(3) (Pdb) cont (0, 3) >>>
Функции, которые преобразуют doctest в код Python и, возможно, запускают полученный код под отладчиком:
- doctest.script_from_examples(s)¶
Преобразует текст с примерами в скрипт.
Аргумент s – это строка, содержащая примеры doctest. Строка преобразуется в скрипт Python, где примеры doctest из s преобразуются в обычный код, а всё остальное – в комментарии Python. Сгенерированный скрипт возвращается в виде строки. Например,
import doctest print(doctest.script_from_examples(r""" Set x and y to 1 and 2. >>> x, y = 1, 2 Print their sum: >>> print(x+y) 3 """))
выводит:
# Установите x и y в 1 и 2. x, y = 1, 2 # # Выведите их сумму: print(x+y) # Ожидаемый результат: ## 3
Эта функция используется внутренне другими функциями (см. ниже), но также может быть полезна, когда требуется преобразовать интерактивный сеанс Python в скрипт Python.
- doctest.testsource(module, name)¶
Преобразует доктест объекта в скрипт.
Аргумент module – это объект модуля или точечное имя модуля, содержащего объект, doctest-ы которого представляют интерес. Аргумент name – это имя (внутри модуля) объекта с интересующими doctest-ами. Результат – строка, содержащая docstring этого объекта, преобразованный в скрипт Python, как описано выше для script_from_examples(). Например, если модуль a.py содержит функцию верхнего уровня f(), то
import a, doctest print(doctest.testsource(a, "a.f"))
выводит скриптовую версию docstring функции f(), где doctest-ы преобразованы в код, а остальное помещено в комментарии.
- doctest.debug(module, name, pm=False)¶
Отладка доктестов для объекта.
Аргументы module и name те же, что и для функции testsource() выше. Синтезированный скрипт Python для docstring указанного объекта записывается во временный файл, а затем этот файл запускается под управлением отладчика Python, pdb.
Поверхностная копия module.__dict__ используется как для локального, так и для глобального контекста выполнения.
Необязательный аргумент pm определяет, используется ли посмертная отладка. Если pm имеет истинное значение, скрипт выполняется напрямую, и отладчик подключается только в том случае, если скрипт завершается из-за необработанного исключения. Если это происходит, вызывается посмертная отладка через pdb.post_mortem() с передачей объекта traceback из необработанного исключения. Если pm не указан или равен false, скрипт выполняется под отладчиком с самого начала, путём передачи соответствующего вызова exec() в pdb.run().
- doctest.debug_src(src, pm=False, globs=None)¶
Отладка доктестов в строке.
Эта функция аналогична debug() выше, за исключением того, что строка с примерами doctest задаётся напрямую через аргумент src.
Необязательный аргумент pm имеет тот же смысл, что и в функции debug() выше.
Необязательный аргумент globs задаёт словарь, используемый как в качестве локального, так и глобального контекста выполнения. Если он не указан или равен None, используется пустой словарь. Если указан, используется поверхностная копия словаря.
Класс DebugRunner и специальные исключения, которые он может вызывать, представляют наибольший интерес для разработчиков тестовых фреймворков, и здесь будут лишь кратко описаны. Подробнее см. в исходном коде, особенно в docstring класса DebugRunner (который сам является doctest!).
- class doctest.DebugRunner(checker=None, verbose=None, optionflags=0)¶
Подкласс DocTestRunner, который вызывает исключение сразу при обнаружении несоответствия. Если происходит неожиданное исключение, вызывается исключение UnexpectedException, содержащее тест, пример и исходное исключение. Если вывод не совпадает, вызывается исключение DocTestFailure, содержащее тест, пример и фактический вывод.
Информацию о параметрах конструктора и методах см. в документации DocTestRunner в разделе Advanced 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().
26.2.8. Трибуна¶Soapbox
Как уже упоминалось во введении, doctest со временем приобрёл три основных назначения:
- Проверка примеров в docstrings.
- Регрессионное тестирование.
- Исполняемая документация / литературное тестирование.
Эти варианты использования предъявляют разные требования, и важно их различать. В частности, наполнение строк документации запутанными тестовыми примерами приводит к плохой документации.
При написании docstring-строки следует тщательно выбирать примеры. Это искусство, которому нужно научиться – сначала оно может казаться неестественным. Примеры должны приносить реальную пользу документации. Хороший пример часто может заменить много слов. Если подойти к делу с умом, примеры станут бесценными для пользователей и многократно окупят время, потраченное на их сбор, с годами, когда всё меняется. Меня до сих пор поражает, как часто один из моих примеров doctest перестаёт работать после «безобидного» изменения.
Doctest также отлично подходит для регрессионного тестирования, особенно если не жалеть пояснительного текста. Чередуя прозу и примеры, гораздо легче отслеживать, что именно тестируется и почему. Когда тест падает, хороший текст помогает быстрее понять, в чём проблема и как её исправить. Конечно, можно писать развёрнутые комментарии в коде тестов, но мало кто это делает. Многие заметили, что использование doctest вместо этого приводит к гораздо более понятным тестам. Возможно, просто потому, что в doctest писать прозу немного легче, чем код, а писать комментарии в коде – немного сложнее. Но я думаю, дело не только в этом: естественный подход при написании теста на основе doctest – это желание объяснить тонкости своего программного обеспечения и проиллюстрировать их примерами. Это, в свою очередь, естественным образом приводит к тестовым файлам, которые начинаются с самых простых функций и логически переходят к осложнениям и крайним случаям. В результате получается связное повествование, а не набор разрозненных функций, тестирующих отдельные фрагменты функциональности, казалось бы, случайным образом. Это другой подход, и он даёт другие результаты, стирая грань между тестированием и объяснением.
Регрессионное тестирование лучше всего проводить в отдельных объектах или файлах. Есть несколько вариантов организации тестов:
- Создавайте текстовые файлы, содержащие тестовые примеры в виде интерактивных примеров, и проверяйте эти файлы с помощью testfile() или DocFileSuite(). Этот подход рекомендуется, хотя его проще всего применять в новых проектах, изначально спроектированных для работы с doctest.
- Определяйте функции с именами вида _regrtest_topic, состоящие из одной строки документации, содержащей тестовые примеры для указанных тем. Такие функции можно включить в тот же файл, что и модуль, или вынести в отдельный тестовый файл.
- Определите словарь __test__, отображающий темы регрессионного тестирования в строки документации, содержащие тестовые примеры.
Сноски
| [1] | Примеры, содержащие как ожидаемый вывод, так и исключение, не поддерживаются. Попытка угадать, где заканчивается одно и начинается другое, слишком чревата ошибками и приводит к запутанным тестам. |