doctest
— Test interactive Python examples¶
Вихідний код: 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.
$
That’s all you need to know to start making productive use of doctest
!
Jump in. The following sections provide full details. Note that there are many
examples of doctests in the standard Python test suite and libraries.
Especially useful examples can be found in the standard test file
Lib/test/test_doctest/test_doctest.py
.
Просте використання: перевірка прикладів у Docstrings¶
The simplest way to start using doctest (but not necessarily the way you’ll
continue to do it) is to end each module M
with:
if __name__ == "__main__":
import doctest
doctest.testmod()
doctest
then examines docstrings in module M
.
Запуск модуля як сценарію призводить до виконання та перевірки прикладів у рядках документів:
python M.py
Це не відображатиме нічого, якщо приклад не буде невдалим, у цьому випадку невдалий(і) приклад(и) і причину(и) помилки(и) друкуються в stdout, а останній рядок виводу буде ***Test Помилка*** N невдач.
, де 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()
нічого не відображатиме, якщо приклад не вдасться. Якщо приклад не вдається, то невдалий(і) приклад(и) і причина(и) невдачі(ів) друкуються у stdout, використовуючи той самий формат, що й testmod()
.
За замовчуванням testfile()
шукає файли в каталозі викликаючого модуля. Перегляньте розділ Базовий API для опису необов’язкових аргументів, які можна використовувати, щоб наказати йому шукати файли в інших місцях.
Подібно до testmod()
, докладність testfile()
можна встановити за допомогою перемикача командного рядка -v
або за допомогою додаткового аргументу ключового слова verbose.
Існує також ярлик командного рядка для запуску testfile()
. Ви можете наказати інтерпретатору Python запустити модуль doctest безпосередньо зі стандартної бібліотеки та передати імена файлів у командному рядку:
python -m doctest -v example.txt
Оскільки назва файлу не закінчується на .py
, doctest
робить висновок, що його потрібно запускати з testfile()
, а не testmod()
.
Для отримання додаткової інформації про testfile()
див. розділ Базовий API.
Як це працює¶
У цьому розділі детально розглядається, як працює doctest: які рядки документації він переглядає, як він знаходить інтерактивні приклади, який контекст виконання він використовує, як він обробляє винятки та як прапорці параметрів можна використовувати для керування його поведінкою. Це інформація, яку вам потрібно знати, щоб написати приклади doctest; для отримання інформації про фактичний запуск doctest на цих прикладах дивіться наступні розділи.
Які рядки документів перевіряються?¶
Здійснюється пошук у рядку документів модуля та всіх рядках документів функцій, класів і методів. Об’єкти, імпортовані в модуль, не шукаються.
In addition, there are cases when you want tests to be part of a module but not part
of the help text, which requires that the tests not be included in the docstring.
Doctest looks for a module-level variable called __test__
and uses it to locate other
tests. If M.__test__
exists, it must be a dict, and each
entry maps a (string) name to a function object, class object, or string.
Function and class object docstrings found from M.__test__
are searched, and
strings are treated as if they were docstrings. In output, a key K
in
M.__test__
appears with name M.__test__.K
.
For example, place this block of code at the top of example.py
:
__test__ = {
'numbers': """
>>> factorial(6)
720
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
"""
}
The value of example.__test__["numbers"]
will be treated as a
docstring and all the tests inside it will be run. It is
important to note that the value can be mapped to a function,
class object, or module; if so, doctest
searches them recursively for docstrings, which are then scanned for tests.
Будь-які знайдені класи рекурсивно шукаються подібним чином, щоб перевірити рядки документів у їх методах і вкладених класах.
Як розпізнаються приклади Docstring?¶
У більшості випадків копіювання та вставлення сеансу інтерактивної консолі працює добре, але doctest не намагається зробити точну емуляцію будь-якої конкретної оболонки Python.
>>> # comments are ignored
>>> x = 12
>>> x
12
>>> if x == 13:
... print("yes")
... else:
... print("no")
... print("NO")
... print("NO!!!")
...
no
NO
NO!!!
>>>
Будь-який очікуваний вихід має слідувати одразу за останнім '>>> '
або '... '
рядком, що містить код, і очікуваний вихід (якщо такий є) поширюється на наступний '>>> "
або рядок із пробілами.
Дрібний шрифт:
Очікуваний вихід не може містити рядок із пробілами, оскільки такий рядок використовується для сигналізації про кінець очікуваного виведення. Якщо очікуваний результат містить порожній рядок, додайте
<BLANKLINE>
у прикладі doctest у кожному місці, де очікується порожній рядок.Усі жорсткі символи табуляції розгорнуті на пробіли за допомогою позицій табуляції з 8 стовпців. Вкладки у вихідних даних, згенерованих тестованим кодом, не змінюються. Оскільки будь-які жорсткі вкладки у вихідних даних зразка розгорнуті, це означає, що якщо вивід коду містить жорсткі вкладки, єдиний спосіб проходження doctest — це параметр
NORMALIZE_WHITESPACE
або директива в ефекті. Крім того, тест можна переписати, щоб зафіксувати результат і порівняти його з очікуваним значенням як частину тесту. Таку обробку вкладок у вихідному коді було досягнуто шляхом проб і помилок, і це виявилося найменш схильним до помилок способом обробки. Можна використати інший алгоритм для обробки вкладок, написавши спеціальний класDocTestParser
.Вихідні дані в stdout фіксуються, але не виводяться в stderr (зворотні дані за винятками фіксуються іншим способом).
Якщо ви продовжуєте рядок через зворотну косу риску в інтерактивному сеансі або з будь-якої іншої причини використовуєте зворотну косу риску, вам слід використовувати необроблений рядок документації, який збереже ваші зворотні косі риски точно так, як ви їх вводите:
>>> def f(x): ... r'''Backslashes in a raw docstring: m\n''' >>> print(f.__doc__) Backslashes in a raw docstring: m\n
В іншому випадку зворотна коса риска буде інтерпретуватися як частина рядка. Наприклад,
\n
вище буде інтерпретовано як символ нового рядка. Крім того, ви можете подвоїти кожну зворотну косу риску у версії doctest (і не використовувати необроблений рядок):>>> def f(x): ... '''Backslashes in a raw docstring: m\\n''' >>> print(f.__doc__) Backslashes in a raw docstring: m\n
Початковий стовпець не має значення:
>>> assert "Easy!" >>> import math >>> math.floor(1.9) 1
і стільки початкових пробільних символів видаляються з очікуваного результату, скільки було показано в початковому рядку
'>>> '
, який розпочав приклад.
Що таке контекст виконання?¶
By default, each time doctest
finds a docstring to test, it uses a
shallow copy of M
’s globals, so that running tests doesn’t change the
module’s real globals, and so that one test in M
can’t leave behind
crumbs that accidentally allow another test to work. This means examples can
freely use any names defined at top-level in M
, and names defined earlier
in the docstring being run. Examples cannot see names defined in other
docstrings.
Ви можете примусово використовувати свій власний 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
Цей doctest завершується успішно, якщо виникає ValueError
, а list.remove(x): x немає в деталях списку
, як показано.
Очікуваний вихід для винятку повинен починатися із заголовка трасування, який може бути будь-яким із наступних двох рядків із таким самим відступом, як і перший рядок прикладу:
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
Зауважте, що трейсбеки обробляються дуже спеціально. Зокрема, у переписаному прикладі використання ...
не залежить від параметра ELLIPSIS
doctest. Багатокрапка в цьому прикладі може бути пропущена, або з таким же успіхом може бути трьома (чи трьома сотнями) комами чи цифрами, або транскриптом сценки Монті Пайтона з відступом.
Деякі деталі, які ви повинні прочитати один раз, але не повинні запам’ятовувати:
Doctest не може здогадатися, чи ваш очікуваний результат отримано від відстеження виняткової ситуації чи від звичайного друку. Так, наприклад, приклад, який очікує
ValueError: 42 є простим
, буде передано незалежно від того, чиValueError
справді викликано, чи приклад просто друкує цей текст трасування. На практиці звичайний вихід рідко починається з рядка заголовка трасування, тому це не створює реальних проблем.Кожен рядок стека зворотного відстеження (якщо він є) повинен мати відступ далі, ніж перший рядок прикладу, або починатися з небуквено-цифрового символу. Перший рядок після заголовка зворотного відстеження з однаковим відступом і починається з буквено-цифрового символу вважається початком деталей винятку. Звичайно, це правильно для справжнього відстеження.
Коли вказано параметр doctest
IGNORE_EXCEPTION_DETAIL
, все, що йде після крайньої лівої двокрапки, і будь-яка інформація модуля в назві винятку ігнорується.Інтерактивна оболонка пропускає рядок заголовка трасування для деяких
SyntaxError
s. Але doctest використовує рядок заголовка трасування, щоб відрізнити винятки від невинятків. Тож у тих рідкісних випадках, коли вам потрібно перевіритиSyntaxError
, який пропускає заголовок трасування, вам потрібно буде вручну додати рядок заголовка трасування до вашого тестового прикладу.
For some exceptions, Python displays the position of the error using
^
markers and tildes:>>> 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'
Прапори параметрів¶
Кілька прапорців параметрів контролюють різні аспекти поведінки doctest. Символічні імена для прапорів надаються як константи модуля, які можна порозрядно об’єднати АБО разом і передати до різних функцій. Імена також можна використовувати в директивах doctest, і можуть бути передані в інтерфейс командного рядка doctest через опцію -o
.
Нове в версії 3.4: Параметр командного рядка -o
.
Перша група параметрів визначає семантику тесту, контролюючи аспекти того, як doctest вирішує, чи фактичний результат відповідає очікуваному результату прикладу:
- doctest.DONT_ACCEPT_TRUE_FOR_1¶
За замовчуванням, якщо очікуваний вихідний блок містить лише
1
, фактичний вихідний блок, що містить лише1
або лишеTrue
, вважається збігом, і аналогічно для0
протиFalse
. Якщо вказаноDONT_ACCEPT_TRUE_FOR_1
, жодна заміна не дозволяється. Поведінка за замовчуванням відповідає тому, що Python змінив тип повернення багатьох функцій з цілого на логічний; doctests, які очікують виводу «маленького цілого», все ще працюють у цих випадках. Цей варіант, ймовірно, зникне, але не через кілька років.
- doctest.DONT_ACCEPT_BLANKLINE¶
За замовчуванням, якщо очікуваний блок виводу містить рядок, що містить лише рядок
<BLANKLINE>
, тоді цей рядок відповідатиме порожньому рядку у фактичному виведенні. Оскільки справді порожній рядок розмежовує очікуваний вихід, це єдиний спосіб повідомити, що очікується порожній рядок. Якщо вказаноDONT_ACCEPT_BLANKLINE
, ця заміна не дозволяється.
- doctest.NORMALIZE_WHITESPACE¶
Якщо вказано, усі послідовності пробілів (пробілі та нові рядки) розглядаються як однакові. Будь-яка послідовність пробілів у очікуваному виводі відповідатиме будь-якій послідовності пробілів у фактичному виведенні. За замовчуванням пробіли мають точно збігатися.
NORMALIZE_WHITESPACE
особливо корисний, коли рядок очікуваного результату дуже довгий, і ви хочете обернути його між кількома рядками у своєму джерелі.
- doctest.ELLIPSIS¶
Якщо вказано, маркер еліпса (
...
) в очікуваному виведенні може збігатися з будь-яким підрядком у фактичному виведенні. Це включає в себе підрядки, які охоплюють межі рядка, і порожні підрядки, тому найкраще використовувати це просто. Складне використання може призвести до тих самих типів «ой, це збігається занадто багато!» сюрпризів, до яких схильний.*
у регулярних виразах.
- doctest.IGNORE_EXCEPTION_DETAIL¶
When specified, doctests expecting exceptions pass so long as an exception of the expected type is raised, even if the details (message and fully qualified exception name) don’t match.
For example, an example expecting
ValueError: 42
will pass if the actual exception raised isValueError: 3*14
, but will fail if, say, aTypeError
is raised instead. It will also ignore any fully qualified name included before the exception class, which can vary between implementations and versions of Python and the code/libraries in use. Hence, all three of these variations will work with the flag specified:>>> raise Exception('message') Traceback (most recent call last): Exception: message >>> raise Exception('message') Traceback (most recent call last): builtins.Exception: message >>> raise Exception('message') Traceback (most recent call last): __main__.Exception: message
Зауважте, що
ELLIPSIS
також можна використовувати для ігнорування деталей повідомлення про винятки, але такий тест все одно може завершитися невдачею залежно від того, чи присутнє ім’я модуля чи воно точно збігається.Змінено в версії 3.2:
IGNORE_EXCEPTION_DETAIL
тепер також ігнорує будь-яку інформацію, пов’язану з модулем, що містить виняток, що тестується.
- doctest.SKIP¶
Якщо вказано, не запускати приклад взагалі. Це може бути корисним у контекстах, де приклади doctest служать як документацією, так і тестовими випадками, і приклад слід включити з метою документації, але не слід перевіряти. Наприклад, результат прикладу може бути випадковим; або приклад може залежати від ресурсів, які були б недоступні для тестового драйвера.
Прапор SKIP також можна використовувати для тимчасового «закоментування» прикладів.
- doctest.COMPARISON_FLAGS¶
Бітова маска або об’єднання всіх прапорів порівняння вище.
Друга група параметрів контролює, як повідомляється про помилки тесту:
- doctest.REPORT_UDIFF¶
Якщо вказано, помилки, які включають багаторядкові очікувані та фактичні виходи, відображаються за допомогою уніфікованої різниці.
- doctest.REPORT_CDIFF¶
Якщо вказано, помилки, які включають багаторядкові очікувані та фактичні результати, відображатимуться за допомогою контекстної різниці.
- doctest.REPORT_NDIFF¶
Якщо вказано, відмінності обчислюються за допомогою
difflib.Differ
, використовуючи той самий алгоритм, що й популярна утилітаndiff.py
. Це єдиний метод, який позначає відмінності всередині ліній, а також між лініями. Наприклад, якщо рядок очікуваного виводу містить цифру1
, а фактичний вивід містить літеруl
, рядок вставляється з кареткою, що позначає невідповідні позиції стовпців.
- doctest.REPORT_ONLY_FIRST_FAILURE¶
Якщо вказано, відобразити перший невдалий приклад у кожному документотесті, але придушити вихід для всіх решти прикладів. Це завадить doctest повідомляти про правильні приклади, які вийшли з ладу через попередні помилки; але це також може приховати некоректні приклади, які завершуються невдачею незалежно від першої невдачі. Якщо вказано
REPORT_ONLY_FIRST_FAILURE
, інші приклади все ще виконуються та враховуються до загальної кількості повідомлених про помилки; пригнічується тільки вихід.
- doctest.FAIL_FAST¶
Якщо вказано, вийти після першого невдалого прикладу та не намагатися запустити решту прикладів. Таким чином, кількість повідомлених помилок становитиме щонайбільше 1. Цей прапорець може бути корисним під час налагодження, оскільки приклади після першої помилки навіть не створюватимуть вихід налагодження.
Командний рядок doctest приймає опцію
-f
як скорочення-o FAIL_FAST
.Нове в версії 3.4.
- doctest.REPORTING_FLAGS¶
Бітова маска або об’єднання всіх наведених вище позначок звітування.
Існує також спосіб зареєструвати нові назви прапорців параметрів, хоча це не корисно, якщо ви не маєте намір розширити внутрішні doctest
через підкласи:
- doctest.register_optionflag(name)¶
Створіть новий прапор параметра з заданою назвою та поверніть ціле значення нового прапора.
register_optionflag()
можна використовувати під час створення підкласівOutputChecker
абоDocTestRunner
для створення нових параметрів, які підтримуються вашими підкласами.register_optionflag()
завжди слід викликати за допомогою такої ідіоми:MY_FLAG = register_optionflag('MY_FLAG')
Директиви¶
Директиви Doctest можна використовувати для зміни прапорців параметрів для окремого прикладу. Директиви Doctest — це спеціальні коментарі Python, які слідують за вихідним кодом прикладу:
directive ::= "#" "doctest:"directive_options
directive_options ::=directive_option
(","directive_option
)* directive_option ::=on_or_off
directive_option_name
on_or_off ::= "+" | "-" directive_option_name ::= "DONT_ACCEPT_BLANKLINE" | "NORMALIZE_WHITESPACE" | ...
Пробіли не допускаються між «+» або «-» та назвою параметра директиви. Назва опції директиви може бути будь-якою з імен прапорців опції, пояснених вище.
Директиви doctest прикладу змінюють поведінку doctest для цього окремого прикладу. Використовуйте +
, щоб увімкнути вказану поведінку, або -
, щоб вимкнути її.
Наприклад, цей тест проходить:
>>> print(list(range(20))) # doctest: +NORMALIZE_WHITESPACE
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Без директиви це було б невдалим, тому що фактичний вивід не має двох пробілів перед однозначними елементами списку, а також тому, що фактичний вивід містить один рядок. Цей тест також проходить, і для цього також потрібна директива:
>>> print(list(range(20))) # doctest: +ELLIPSIS
[0, 1, ..., 18, 19]
В одному фізичному рядку можна використовувати кілька директив, розділених комами:
>>> print(list(range(20))) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]
Якщо для одного прикладу використовується кілька коментарів директиви, вони об’єднуються:
>>> print(list(range(20))) # doctest: +ELLIPSIS
... # doctest: +NORMALIZE_WHITESPACE
[0, 1, ..., 18, 19]
Як показано в попередньому прикладі, ви можете додати до свого прикладу рядки ...
, які містять лише директиви. Це може бути корисно, коли приклад надто довгий, щоб директива могла зручно розміститися в одному рядку:
>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40)))
... # doctest: +ELLIPSIS
[0, ..., 4, 10, ..., 19, 30, ..., 39]
Зауважте, що оскільки всі параметри вимкнено за замовчуванням, а директиви застосовуються лише до прикладу, у якому вони з’являються, увімкнення параметрів (через +
у директиві) зазвичай є єдиним значущим вибором. Однак позначки параметрів також можна передати функціям, які запускають 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
безпечні на всіх платформах, і я часто створюю приклади doctest для отримання чисел такої форми:
>>> 3./4 # utterly safe
0.75
Прості дроби також легше зрозуміти людям, і це покращує документацію.
Базовий 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)¶
Усі аргументи, окрім ім’я файлу, є необов’язковими та мають бути вказані у формі ключового слова.
Тестові приклади у файлі з назвою ім’я_файлу. Повернення
(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, який буде використовуватися як глобальні під час виконання прикладів. Для doctest створюється нова поверхнева копія цього dict, тому її приклади починаються з чистого аркуша. За замовчуванням або якщо
None
, використовується новий порожній dict.Необов’язковий аргумент extraglobs дає dict, об’єднаний із глобальними значеннями, які використовуються для виконання прикладів. Це працює як
dict.update()
: якщо globs і extraglobs мають спільний ключ, пов’язане значення в extraglobs з’являється в комбінованому dict. За замовчуванням або якщоNone
, додаткові глобальні значення не використовуються. Це розширена функція, яка дозволяє параметризувати doctests. Наприклад, doctest можна написати для базового класу, використовуючи загальну назву для класу, а потім повторно використовувати для тестування будь-якої кількості підкласів, передавши extraglobs dict, що відображає загальну назву на підклас, який потрібно перевірити.Додатковий аргумент verbose друкує багато речей, якщо true, і друкує лише помилки, якщо false; за замовчуванням або якщо
None
, це істина тоді і тільки якщо'-v''
є вsys.argv
.Необов’язковий аргумент report друкує підсумок у кінці, якщо значення true, інакше нічого не друкує в кінці. У багатослівному режимі резюме є детальним, інакше резюме є дуже коротким (насправді порожнім, якщо всі тести пройдено).
Необов’язковий аргумент optionflags (значення за замовчуванням 0) приймає побітове АБО прапорів параметрів. Дивіться розділ Прапори параметрів.
Додатковий аргумент raise_on_error за умовчанням має значення false. Якщо істина, виняток виникає після першої помилки або несподіваного винятку в прикладі. Це дозволяє посмертно виправляти помилки. Типовою поведінкою є продовження виконання прикладів.
Необов’язковий аргумент parser визначає
DocTestParser
(або підклас), який слід використовувати для отримання тестів із файлів. За замовчуванням використовується звичайний аналізатор (тобтоDocTestParser()
).Необов’язковий аргумент encoding визначає кодування, яке слід використовувати для перетворення файлу в Юнікод.
- 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__
.Also test examples reachable from dict
m.__test__
, if it exists.m.__test__
maps names (strings) to functions, classes and strings; function and class docstrings are searched for examples; strings are searched directly, as if they were docstrings.Пошук здійснюється лише в рядках документів, приєднаних до об’єктів, що належать модулю m.
Повернення
(failure_count, test_count)
.Необов’язковий аргумент name дає назву модуля; за замовчуванням або, якщо
None
,m.__name__
використовується.Optional argument exclude_empty defaults to false. If true, objects for which no doctests are found are excluded from consideration. The default is a backward compatibility hack, so that code still using
doctest.master.summarize
in conjunction withtestmod()
continues to get output for objects with no tests. The exclude_empty argument to the newerDocTestFinder
constructor defaults to true.Необов’язкові аргументи extraglobs, verbose, report, optionflags, raise_on_error і globs такі самі, як і для функції
testfile()
вище, за винятком того, що globs за умовчанням має значенняm .__dict__
.
- doctest.run_docstring_examples(f, globs, verbose=False, name='NoName', compileflags=None, optionflags=0)¶
Тестові приклади, пов’язані з об’єктом f; наприклад, f може бути рядком, модулем, функцією або об’єктом класу.
Неглибока копія аргументу словника globs використовується для контексту виконання.
Необов’язковий аргумент ім’я використовується в повідомленнях про помилку та за умовчанням має значення
"NoName"
.Якщо додатковий аргумент verbose має значення true, вихідні дані генеруються, навіть якщо немає помилок. За замовчуванням вихідні дані генеруються лише у випадку помилки прикладу.
Необов’язковий аргумент compileflags надає набір прапорів, які має використовувати компілятор Python під час виконання прикладів. За замовчуванням або якщо
None
, прапори виводяться відповідно до набору майбутніх функцій, знайдених у globs.Необов’язковий аргумент optionflags працює як для функції
testfile()
вище.
Unittest API¶
As your collection of doctest’ed modules grows, you’ll want a way to run all
their doctests systematically. doctest
provides two functions that can
be used to create unittest
test suites from modules and text files
containing doctests. To integrate with unittest
test discovery, include
a load_tests function in your test module:
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
.The returned
unittest.TestSuite
is to be run by the unittest framework and runs the interactive examples in each file. If an example in any file fails, then the synthesized unit test fails, and afailureException
exception is raised showing the name of the file containing the test and a (sometimes approximate) line number.Передайте один або кілька шляхів (у вигляді рядків) до текстових файлів, які потрібно перевірити.
Параметри можуть бути надані як аргументи ключових слів:
Необов’язковий аргумент 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 вказує параметри тесту за замовчуванням для тестів, створені шляхом об’єднання окремих прапорців параметрів. Дивіться розділ Прапори параметрів. Перегляньте функцію
set_unittest_reportflags()
нижче, щоб дізнатися про кращий спосіб налаштування параметрів звітування.Необов’язковий аргумент parser визначає
DocTestParser
(або підклас), який слід використовувати для отримання тестів із файлів. За замовчуванням використовується звичайний аналізатор (тобтоDocTestParser()
).Необов’язковий аргумент encoding визначає кодування, яке слід використовувати для перетворення файлу в Юнікод.
Глобальний
__file__
додається до глобалів, наданих до doctests, завантажених із текстового файлу за допомогоюDocFileSuite()
.
- doctest.DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None)¶
Перетворіть тести doctest для модуля на
unittest.TestSuite
.The returned
unittest.TestSuite
is to be run by the unittest framework and runs each doctest in the module. If any of the doctests fail, then the synthesized unit test fails, and afailureException
exception is raised showing the name of the file containing the test and a (sometimes approximate) line number.Необов’язковий аргумент module надає модуль для тестування. Це може бути об’єкт модуля або ім’я модуля (можливо з крапками). Якщо не вказано, використовується модуль, який викликає цю функцію.
Додатковий аргумент globs — це словник, що містить початкові глобальні змінні для тестів. Для кожного тесту створюється нова копія цього словника. За замовчуванням globs — це новий порожній словник.
Необов’язковий аргумент extraglobs визначає додатковий набір глобальних змінних, який об’єднується в globs. За замовчуванням додаткові глобальні значення не використовуються.
Необов’язковий аргумент test_finder — це об’єкт
DocTestFinder
(або замінник), який використовується для вилучення тестів документів із модуля.Необов’язкові аргументи setUp, tearDown і optionflags такі самі, як і для функції
DocFileSuite()
вище.Ця функція використовує ту саму техніку пошуку, що й
testmod()
.Змінено в версії 3.5:
DocTestSuite()
повертає порожнійunittest.TestSuite
, якщо module не містить рядків документів, замість того, щоб викликатиValueError
.
- exception doctest.failureException¶
When doctests which have been converted to unit tests by
DocFileSuite()
orDocTestSuite()
fail, this exception is raised showing the name of the file containing the test and a (sometimes approximate) line number.
Under the covers, DocTestSuite()
creates a unittest.TestSuite
out
of doctest.DocTestCase
instances, and DocTestCase
is a
subclass of unittest.TestCase
. DocTestCase
isn’t documented
here (it’s an internal detail), but studying its code can answer questions about
the exact details of unittest
integration.
Similarly, DocFileSuite()
creates a unittest.TestSuite
out of
doctest.DocFileCase
instances, and DocFileCase
is a subclass
of DocTestCase
.
So both ways of creating a unittest.TestSuite
run instances of
DocTestCase
. This is important for a subtle reason: when you run
doctest
functions yourself, you can control the doctest
options in
use directly, by passing option flags to doctest
functions. However, if
you’re writing a unittest
framework, unittest
ultimately controls
when and how tests get run. The framework author typically wants to control
doctest
reporting options (perhaps, e.g., specified by command line
options), but there’s no way to pass options through unittest
to
doctest
test runners.
З цієї причини doctest
також підтримує поняття прапорів звітності doctest
, специфічних для підтримки unittest
, через цю функцію:
- doctest.set_unittest_reportflags(flags)¶
Встановіть прапорці звітів
doctest
для використання.Аргумент flags приймає побітове АБО прапорів параметрів. Дивіться розділ Прапори параметрів. Можна використовувати лише «прапори звітності».
This is a module-global setting, and affects all future doctests run by module
unittest
: therunTest()
method ofDocTestCase
looks at the option flags specified for the test case when theDocTestCase
instance was constructed. If no reporting flags were specified (which is the typical and expected case),doctest
’sunittest
reporting flags are bitwise ORed into the option flags, and the option flags so augmented are passed to theDocTestRunner
instance created to run the doctest. If any reporting flags were specified when theDocTestCase
instance was constructed,doctest
’sunittest
reporting flags are ignored.Функція повертає значення прапорів звітів
unittest
, які діяли до виклику функції.
Розширений API¶
Основний API — це проста оболонка, призначена для полегшення використання doctest. Він досить гнучкий і повинен відповідати потребам більшості користувачів; однак, якщо вам потрібен більш детальний контроль над тестуванням або ви бажаєте розширити можливості doctest, тоді вам слід скористатися розширеним API.
Розширений API обертається навколо двох класів контейнерів, які використовуються для зберігання інтерактивних прикладів, отриманих із випадків doctest:
Example
: один statement Python у поєднанні з очікуваним результатом.DocTest
: колекціяExample
, зазвичай витягнутих з одного рядка документа або текстового файлу.
Додаткові класи обробки визначені для пошуку, аналізу та запуску та перевірки прикладів doctest:
DocTestFinder
: знаходить усі рядки документів у певному модулі та використовуєDocTestParser
для створенняDocTest
з кожного рядка документів, який містить інтерактивні приклади.DocTestParser
: створює об’єктDocTest
із рядка (наприклад, рядка документації об’єкта).DocTestRunner
: Виконує приклади вDocTest
і використовуєOutputChecker
для перевірки їх результату.OutputChecker
: порівнює фактичні результати прикладу doctest з очікуваними результатами та вирішує, чи вони збігаються.
Відносини між цими класами обробки підсумовано на наступній діаграмі:
list of:
+------+ +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+ | ^ +---------+ | ^ (printed)
| | | Example | | |
v | | ... | v |
DocTestParser | Example | OutputChecker
+---------+
Об’єкти DocTest¶
- class doctest.DocTest(examples, globs, name, filename, lineno, docstring)¶
Набір прикладів doctest, які слід запускати в одному просторі імен. Аргументи конструктора використовуються для ініціалізації однойменних атрибутів.
DocTest
визначає такі атрибути. Вони ініціалізуються конструктором і не повинні змінюватися безпосередньо.- examples¶
Список об’єктів
Example
, що кодують окремі інтерактивні приклади Python, які мають виконуватися цим тестом.
- globs¶
Простір імен (він же глобальні), у якому слід запускати приклади. Це словник, що зіставляє імена зі значеннями. Будь-які зміни в просторі імен, внесені прикладами (такі як зв’язування нових змінних), будуть відображені в
globs
після виконання тесту.
- name¶
Ім’я рядка, що ідентифікує
DocTest
. Як правило, це ім’я об’єкта або файлу, з якого було отримано тест.
- filename¶
Ім’я файлу, з якого було видобуто цей
DocTest
; абоNone
, якщо назва файлу невідома, або якщоDocTest
не було видобуто з файлу.
- lineno¶
Номер рядка в
filename
, де починається цейDocTest
, абоNone
, якщо номер рядка недоступний. Цей номер рядка відраховується від нуля відносно початку файлу.
- docstring¶
Рядок, з якого було отримано тест, або
None
, якщо рядок недоступний, або якщо тест не було вилучено з рядка.
Приклад об’єктів¶
- 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¶
A dictionary mapping from option flags to
True
orFalse
, which is used to override default options for this example. Any option flags not contained in this dictionary are left at their default value (as specified by theDocTestRunner
’s optionflags). By default, no options are set.
Об’єкти DocTestFinder¶
- class doctest.DocTestFinder(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)¶
Клас обробки, який використовується для вилучення
DocTest
s, які мають відношення до даного об’єкта, з його рядка документації та рядків документації об’єктів, які містяться в ньому.DocTest
s можна отримати з модулів, класів, функцій, методів, статичних методів, методів класів і властивостей.Необов’язковий аргумент verbose можна використовувати для відображення об’єктів, які шукав шукач. За замовчуванням значення False (без виведення).
Необов’язковий аргумент parser визначає об’єкт
DocTestParser
(або замінник), який використовується для вилучення тестів документів із рядків документів.Якщо необов’язковий аргумент recurse має значення false, тоді
DocTestFinder.find()
перевірятиме лише даний об’єкт, а не будь-які об’єкти, що містяться.Якщо необов’язковий аргумент exclude_empty має значення false, тоді
DocTestFinder.find()
включатиме тести для об’єктів із порожніми рядками документів.DocTestFinder
визначає такий метод:- find(obj[, name][, module][, globs][, extraglobs])¶
Повертає список
DocTest
, які визначені рядком документації obj або будь-якими рядками документів, що містяться в ньому.Необов’язковий аргумент name визначає ім’я об’єкта; це ім’я буде використано для створення імен для повернутих
DocTest
s. Якщо name не вказано, тоді використовуєтьсяobj.__name__
.Необов’язковий параметр module — це модуль, який містить заданий об’єкт. Якщо модуль не вказано або має значення
None
, то засіб пошуку тестів спробує автоматично визначити правильний модуль. Використовується модуль об’єкта:Як простір імен за замовчуванням, якщо globs не вказано.
Щоб DocTestFinder не видобував DocTests з об’єктів, імпортованих з інших модулів. (Об’єкти, що містяться з модулями, відмінними від module, ігноруються.)
Щоб знайти ім’я файлу, що містить об’єкт.
Щоб допомогти знайти номер рядка об’єкта в його файлі.
Якщо module має значення
False
, спроба знайти модуль не буде зроблена. Це незрозуміло, використовується переважно під час тестування самого doctest: якщо module має значенняFalse
абоNone
, але не може бути знайдено автоматично, тоді всі об’єкти вважаються такими, що належать до (неіснуючого) модуль, тому всі об’єкти, що містяться, будуть (рекурсивно) шукатися для тестів документів.Глобальні значення для кожного
DocTest
утворюються шляхом об’єднання globs і extraglobs (прив’язки в extraglobs замінюють прив’язки в globs). Для кожногоDocTest
створюється нова копія глобального словника. Якщо globs не вказано, тоді за замовчуванням використовується __dict__ модуля, якщо вказано, або{}
інакше. Якщо extraglobs не вказано, то за замовчуванням буде{}
.
Об’єкти DocTestParser¶
- class doctest.DocTestParser¶
Клас обробки, який використовується для отримання інтерактивних прикладів із рядка та використання їх для створення об’єкта
DocTest
.DocTestParser
визначає такі методи:- get_doctest(string, globs, name, filename, lineno)¶
Витягніть усі приклади doctest із заданого рядка та зберіть їх у об’єкт
DocTest
.globs, name, filename і lineno є атрибутами для нового об’єкта
DocTest
. Перегляньте документацію дляDocTest
для отримання додаткової інформації.
- get_examples(string, name='<string>')¶
Витягніть усі приклади doctest із заданого рядка та поверніть їх як список об’єктів
Example
. Номери рядків базуються на 0. Необов’язковий аргумент ім’я – це ім’я, що ідентифікує цей рядок і використовується лише для повідомлень про помилки.
- parse(string, name='<string>')¶
Розділіть заданий рядок на приклади та проміжний текст і поверніть їх як список чергування
Example
і рядків. Номери рядків дляExample
s засновані на 0. Необов’язковий аргумент ім’я – це ім’я, що ідентифікує цей рядок і використовується лише для повідомлень про помилки.
Об’єкти DocTestRunner¶
- class doctest.DocTestRunner(checker=None, verbose=None, optionflags=0)¶
Клас обробки, який використовується для виконання та перевірки інтерактивних прикладів у
DocTest
.Порівняння очікуваних і фактичних результатів виконується за допомогою
OutputChecker
. Це порівняння можна налаштувати за допомогою кількох позначок параметрів; дивіться розділ Прапори параметрів для отримання додаткової інформації. Якщо прапорців параметрів недостатньо, порівняння також можна налаштувати, передавши підкласOutputChecker
до конструктора.The test runner’s display output can be controlled in two ways. First, an output function can be passed to
TestRunner.run()
; this function will be called with strings that should be displayed. It defaults tosys.stdout.write
. If capturing the output is not sufficient, then the display output can be also customized by subclassing DocTestRunner, and overriding the methodsreport_start()
,report_success()
,report_unexpected_exception()
, andreport_failure()
.Необов’язковий аргумент ключового слова checker визначає об’єкт
OutputChecker
(або додаткову заміну), який слід використовувати для порівняння очікуваних результатів із фактичними результатами прикладів doctest.Додатковий аргумент ключового слова verbose контролює докладність
DocTestRunner
. Якщо verbose має значенняTrue
, тоді друкується інформація про кожен приклад під час його виконання. Якщо verbose має значенняFalse
, друкуються лише помилки. Якщо verbose не вказано абоNone
, тоді використовується докладний вивід, якщо використовується параметр командного рядка-v
.Необов’язковий аргумент ключового слова optionflags можна використовувати для керування тим, як програма виконання тестів порівнює очікуваний результат із фактичним результатом і як він відображає помилки. Для отримання додаткової інформації див. розділ Прапори параметрів.
DocTestRunner
defines the following methods:- report_start(out, test, example)¶
Повідомте, що тестувальник збирається обробити наведений приклад. Цей метод надається, щоб дозволити підкласам
DocTestRunner
налаштовувати свій вихід; його не слід називати безпосередньо.приклад — це приклад, який буде оброблено. тест — це тест, який містить приклад. out — це функція виводу, яка була передана в
DocTestRunner.run()
.
- report_success(out, test, example, got)¶
Повідомте, що наведений приклад виконано успішно. Цей метод надається, щоб дозволити підкласам
DocTestRunner
налаштовувати свій вихід; його не слід називати безпосередньо.приклад — це приклад, який буде оброблено. got — фактичний вихід із прикладу. тест — це тест, що містить приклад. out — це функція виводу, яка була передана в
DocTestRunner.run()
.
- report_failure(out, test, example, got)¶
Повідомте, що наведений приклад не вдався. Цей метод надається, щоб дозволити підкласам
DocTestRunner
налаштовувати свій вихід; його не слід називати безпосередньо.приклад — це приклад, який буде оброблено. got — фактичний вихід із прикладу. тест — це тест, що містить приклад. out — це функція виводу, яка була передана в
DocTestRunner.run()
.
- report_unexpected_exception(out, test, example, exc_info)¶
Повідомте, що наведений приклад викликав несподіваний виняток. Цей метод надається, щоб дозволити підкласам
DocTestRunner
налаштовувати свій вихід; його не слід називати безпосередньо.приклад — це приклад, який буде оброблено. exc_info — це кортеж, що містить інформацію про неочікуваний виняток (як повертає
sys.exc_info()
). тест — це тест, що містить приклад. out — це функція виводу, яка була передана вDocTestRunner.run()
.
- run(test, compileflags=None, out=None, clear_globs=True)¶
Run the examples in test (a
DocTest
object), and display the results using the writer function out.Приклади запускаються в просторі імен
test.globs
. Якщо clear_globs має значення true (за замовчуванням), цей простір імен буде очищено після виконання тесту, щоб допомогти зі збиранням сміття. Якщо ви хочете перевірити простір імен після завершення тесту, використовуйте clear_globs=False.compileflags надає набір прапорів, які має використовувати компілятор Python під час виконання прикладів. Якщо не вказано, за замовчуванням використовуватиметься набір прапорів майбутнього імпорту, які застосовуються до globs.
The output of each example is checked using the
DocTestRunner
’s output checker, and the results are formatted by theDocTestRunner.report_*()
methods.
- summarize(verbose=None)¶
Print a summary of all the test cases that have been run by this DocTestRunner, and return a named tuple
TestResults(failed, attempted)
.Необов’язковий аргумент verbose контролює, наскільки детальним є резюме. Якщо детальність не вказана, тоді використовується детальність
DocTestRunner
.
Об’єкти OutputChecker¶
- class doctest.OutputChecker¶
Клас, який використовується для перевірки того, чи фактичний результат із прикладу doctest відповідає очікуваному результату.
OutputChecker
визначає два методи:check_output()
, який порівнює задану пару виходів і повертаєTrue
, якщо вони збігаються; іoutput_difference()
, який повертає рядок, що описує різницю між двома результатами.OutputChecker
визначає такі методи:- check_output(want, got, optionflags)¶
Повертає
True
, якщо фактичний результат із прикладу (got) відповідає очікуваному результату (want). Ці рядки завжди вважаються такими, що збігаються, якщо вони ідентичні; але залежно від того, які прапорці параметрів використовує програма виконання тестів, також можливі кілька типів неточної відповідності. Перегляньте розділ Прапори параметрів для отримання додаткової інформації про прапорці параметрів.
- output_difference(example, got, optionflags)¶
Повертає рядок, що описує відмінності між очікуваним виходом для даного прикладу (example) і фактичним виходом (got). optionflags — це набір позначок параметрів, які використовуються для порівняння want і got.
Налагодження¶
Doctest надає кілька механізмів для налагодження прикладів doctest:
Декілька функцій перетворюють doctests на виконувані програми Python, які можна запускати в налагоджувачі Python,
pdb
.Клас
DebugRunner
є підкласомDocTestRunner
, який створює виняток для першого невдалого прикладу, що містить інформацію про цей приклад. Ця інформація може бути використана для виконання посмертного налагодження на прикладі.Випадки
unittest
, згенерованіDocTestSuite()
, підтримують методdebug()
, визначенийunittest.TestCase
.Ви можете додати виклик до
pdb.set_trace()
у прикладі doctest, і ви перейдете до налагоджувача Python, коли цей рядок буде виконано. Потім ви можете перевірити поточні значення змінних і так далі. Наприклад, припустимо, щоa.py
містить лише цей модуль docstring:""" >>> def f(x): ... g(x*2) >>> def g(x): ... print(x+3) ... import pdb; pdb.set_trace() >>> f(3) 9 """
Тоді інтерактивний сеанс Python може виглядати так:
>>> import a, doctest >>> doctest.testmod(a) --Return-- > <doctest a[1]>(3)g()->None -> import pdb; pdb.set_trace() (Pdb) list 1 def g(x): 2 print(x+3) 3 -> import pdb; pdb.set_trace() [EOF] (Pdb) p x 6 (Pdb) step --Return-- > <doctest a[0]>(2)f()->None -> g(x*2) (Pdb) list 1 def f(x): 2 -> g(x*2) [EOF] (Pdb) p x 3 (Pdb) step --Return-- > <doctest a[2]>(1)?()->None -> f(3) (Pdb) cont (0, 3) >>>
Функції, які перетворюють doctests на код Python і, можливо, запускають синтезований код під налагоджувачем:
- doctest.script_from_examples(s)¶
Перетворення тексту з прикладами на сценарій.
Аргумент s — це рядок, що містить приклади doctest. Рядок перетворюється на сценарій Python, де приклади doctest у s перетворюються на звичайний код, а все інше перетворюється на коментарі Python. Згенерований сценарій повертається як рядок. Наприклад,
import doctest print(doctest.script_from_examples(r""" Set x and y to 1 and 2. >>> x, y = 1, 2 Print their sum: >>> print(x+y) 3 """))
дисплеї:
# Set x and y to 1 and 2. x, y = 1, 2 # # Print their sum: print(x+y) # Expected: ## 3
Ця функція використовується внутрішньо іншими функціями (див. нижче), але також може бути корисною, коли ви хочете перетворити інтерактивний сеанс Python на сценарій Python.
- doctest.testsource(module, name)¶
Перетворення doctest для об’єкта на сценарій.
Argument module is a module object, or dotted name of a module, containing the object whose doctests are of interest. Argument name is the name (within the module) of the object with the doctests of interest. The result is a string, containing the object’s docstring converted to a Python script, as described for
script_from_examples()
above. For example, if modulea.py
contains a top-level functionf()
, thenimport a, doctest print(doctest.testsource(a, "a.f"))
prints a script version of function
f()
“s docstring, with doctests converted to code, and the rest placed in comments.
- 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()
вище, за винятком того, що рядок, що містить приклади doctest, вказується безпосередньо через аргумент 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 не збігається з його очікуваним результатом. Аргументи конструктора використовуються для ініціалізації однойменних атрибутів.
DocTestFailure
визначає такі атрибути:
- DocTestFailure.got¶
Фактичний результат прикладу.
- exception doctest.UnexpectedException(test, example, exc_info)¶
Виняток, викликаний
DocTestRunner
, щоб сигналізувати про те, що приклад doctest викликав неочікуваний виняток. Аргументи конструктора використовуються для ініціалізації однойменних атрибутів.
UnexpectedException
визначає такі атрибути:
- UnexpectedException.exc_info¶
Кортеж, що містить інформацію про неочікуваний виняток, яку повертає
sys.exc_info()
.
Мильниця¶
Як згадувалося у вступі, doctest
зріс до трьох основних застосувань:
Перевірка прикладів у рядках документів.
Регресійне тестування.
Виконувана документація / грамотне тестування.
Ці види використання мають різні вимоги, і їх важливо розрізняти. Зокрема, заповнення ваших рядків документації незрозумілими тестовими прикладами створює погану документацію.
Під час написання документаційного рядка обережно вибирайте приклади документаційного рядка. У цьому є певне мистецтво, якому потрібно навчитися — спочатку це може бути неприродним. Приклади повинні додати справжню цінність документації. Хороший приклад часто вартий багатьох слів. Якщо їх робити обережно, приклади будуть безцінні для ваших користувачів і багаторазово окуплять час, потрачений на їх збирання, оскільки роки йдуть і все змінюється. Мене все ще дивує, як часто один із моїх прикладів doctest
перестає працювати після «нешкідливої» зміни.
Doctest також є чудовим інструментом для регресійного тестування, особливо якщо ви не економите на пояснювальному тексті. Перемежовуючи прозу та приклади, стає набагато легше відслідковувати, що насправді перевіряється та чому. Якщо тест провалився, хороша проза може значно полегшити з’ясування проблеми та способи її вирішення. Це правда, що ви можете писати розгорнуті коментарі під час тестування на основі коду, але мало хто з програмістів це робить. Багато хто виявив, що використання підходів doctest натомість призводить до набагато чіткіших тестів. Можливо, це просто тому, що doctest робить написання прози трохи легшим, ніж написання коду, тоді як писати коментарі в коді трохи складніше. Я думаю, що це глибше, ніж просто це: природне ставлення до написання тесту на основі doctest полягає в тому, що ви хочете пояснити тонкощі свого програмного забезпечення та проілюструвати їх прикладами. Це, у свою чергу, природним чином призводить до тестових файлів, які починаються з найпростіших функцій і логічно прогресують до ускладнень і крайніх випадків. Результатом є послідовна розповідь, а не набір ізольованих функцій, які перевіряють окремі фрагменти функціональності, здавалося б, випадковим чином. Це інше ставлення, яке дає інші результати, стираючи різницю між тестуванням і поясненням.
Регресійне тестування найкраще обмежити виділеними об’єктами або файлами. Є кілька варіантів організації тестів:
Напишіть текстові файли, що містять тестові приклади як інтерактивні приклади, і протестуйте файли за допомогою
testfile()
абоDocFileSuite()
. Це рекомендовано, хоча це найлегше зробити для нових проектів, розроблених із самого початку для використання doctest.Визначте функції під назвою
_regrtest_topic
, які складаються з окремих рядків документів, що містять тестові випадки для названих тем. Ці функції можна включити в той самий файл, що й модуль, або відокремити в окремий тестовий файл.Визначте зіставлення словника
__test__
із тем регресійних тестів на рядки документів, що містять тестові приклади.
Якщо ви розмістили свої тести в модулі, модуль сам може бути виконавцем тестів. Коли тест виходить невдалим, ви можете організувати повторний запуск лише невдалого документу, поки ви вирішуєте проблему. Ось мінімальний приклад такого тесту:
if __name__ == '__main__':
import doctest
flags = doctest.REPORT_NDIFF|doctest.FAIL_FAST
if len(sys.argv) > 1:
name = sys.argv[1]
if name in globals():
obj = globals()[name]
else:
obj = __test__[name]
doctest.run_docstring_examples(obj, globals(), name=name,
optionflags=flags)
else:
fail, total = doctest.testmod(optionflags=flags)
print("{} failures out of {} tests".format(fail, total))
Виноски