Spec-Zone.ru › Python 3.11

doctest — Тестирование интерактивных примеров Python

Исходный код: Lib/doctest.py

Модуль doctest ищет фрагменты текста, похожие на интерактивные сессии Python, и затем выполняет эти сессии, чтобы проверить, что они работают точно так, как показано. Существует несколько распространённых способов использования doctest:

  • Проверить, что документация модуля актуальна, убедившись, что все интерактивные примеры по-прежнему работают так, как задокументировано.
  • Провести регрессионное тестирование, проверив, что интерактивные примеры из файла или объекта теста работают как ожидается.
  • Создать учебную документацию для пакета, обильно иллюстрированную примерами ввода-вывода. В зависимости от того, на примерах или экспозиционном тексте делается акцент, это имеет характер «литературоведческого тестирования» или «исполняемой документации».

Вот пример модуля, полный, но небольшой:

"""
This is the "example" module.

The example module supplies one function, factorial().  For example,

>>> factorial(5)
120
"""

def factorial(n):
    """Return the factorial of n, an exact integer >= 0.

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(30)
    265252859812191058636308480000000
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    ValueError: n must be >= 0

    Factorials of floats are OK, but the float must be an exact integer:
    >>> factorial(30.1)
    Traceback (most recent call last):
        ...
    ValueError: n must be exact integer
    >>> factorial(30.0)
    265252859812191058636308480000000

    It must also not be ridiculously large:
    >>> factorial(1e100)
    Traceback (most recent call last):
        ...
    OverflowError: n too large
    """

    import math
    if not n >= 0:
        raise ValueError("n must be >= 0")
    if math.floor(n) != n:
        raise ValueError("n must be exact integer")
    if n+1 == n:  # catch a value like 1e300
        raise OverflowError("n too large")
    result = 1
    factor = 2
    while factor <= n:
        result *= factor
        factor += 1
    return result


if __name__ == "__main__":
    import doctest
    doctest.testmod()

Если вы запустите example.py напрямую из командной строки, doctest сотворит свою магию:

$ python example.py
$

Вывода нет! Это нормально, и это означает, что все примеры отработали. Передайте -v скрипту, и doctest выведет подробный протокол того, что он пытается сделать, а в конце выведет сводку:

$ python example.py -v
Trying:
    factorial(5)
Expecting:
    120
ok
Trying:
    [factorial(n) for n in range(6)]
Expecting:
    [1, 1, 2, 6, 24, 120]
ok

И так далее, в конечном счёте заканчивая:

Trying:
    factorial(1e100)
Expecting:
    Traceback (most recent call last):
        ...
    OverflowError: n too large
ok
2 items passed all tests:
   1 tests in __main__
   8 tests in __main__.factorial
9 tests in 2 items.
9 passed and 0 failed.
Test passed.
$

Это все, что вам нужно знать, чтобы начать продуктивно использовать doctest! Приступайте. В следующих разделах приведены подробности. Обратите внимание, что в стандартном наборе тестов и библиотеках Python содержится много примеров doctest. Особо полезные примеры можно найти в стандартном файле тестов Lib/test/test_doctest.py.

Простое использование: проверка примеров в строках документации

Самый простой способ начать использовать doctest (но не обязательно способ, которым вы будете продолжать это делать) — завершить каждый модуль M следующим образом:

if __name__ == "__main__":
    import doctest
    doctest.testmod()

doctest затем проверяет строки документации в модуле M.

Запуск модуля как скрипта приводит к выполнению и проверке примеров в строках документации:

python M.py

Это ничего не отобразит, если пример не пройдёт, в этом случае не пройденный(ые) пример(ы) и причина(ы) ошибки выводятся в стандартный вывод, а последняя строка вывода — ***Test Failed*** N failures., где N — количество не пройденных примеров.

Запустите его со значением -v вместо этого:

python M.py -v

и на стандартный вывод будет напечатан подробный отчёт обо всех проверенных примерах, а также различные сводки в конце.

Вы можете принудительно включить подробный режим, передав verbose=True в testmod(), или запретить его, передав verbose=False. В любом из этих случаев sys.argv не проверяется testmod() (так что передача -v или её отсутствие не имеет эффекта).

Также существует сокращённый вариант командной строки для запуска testmod(). Вы можете указать интерпретатору Python запустить модуль doctest непосредственно из стандартной библиотеки и передать имена модуля(ей) в командной строке:

python -m doctest -v example.py

Это импортирует example.py как автономный модуль и запускает testmod() на нём. Обратите внимание, что это может не работать правильно, если файл является частью пакета и импортирует другие подмодули из этого пакета.

Для получения дополнительной информации о testmod(), см. раздел Основные API.

Простое использование: проверка примеров в текстовом файле

Ещё одно простое применение doctest — тестирование интерактивных примеров в текстовом файле. Это можно сделать с помощью функции testfile():

import doctest
doctest.testfile("example.txt")

Этот короткий скрипт выполняет и проверяет любые интерактивные примеры Python, содержащиеся в файле example.txt. Содержимое файла обрабатывается как единая большая строка документации; файл не обязательно должен содержать программу Python! Например, возможно, example.txt содержит следующее:

The ``example`` module
======================

Using ``factorial``
-------------------

This is an example text file in reStructuredText format.  First import
``factorial`` from the ``example`` module:

    >>> from example import factorial

Now use it:

    >>> factorial(6)
    120

Запуск doctest.testfile("example.txt") затем обнаруживает ошибку в этой документации:

File "./example.txt", line 14, in example.txt
Failed example:
    factorial(6)
Expected:
    120
Got:
    720

Как и в случае с testmod(), testfile() ничего не отобразит, если пример не пройдёт. Если пример не пройдёт, то не пройденный(ые) пример(ы) и причина(ы) ошибки выводятся в стандартный вывод, используя тот же формат, что и testmod().

По умолчанию, testfile() ищет файлы в каталоге вызывающего модуля. См. раздел Основные API для описания дополнительных аргументов, которые можно использовать, чтобы указать ему искать файлы в других местах.

Как и testmod(), подробность вывода testfile() можно настроить с помощью переключателя командной строки -v или с помощью необязательного ключевого аргумента verbose.

Также существует сокращённый вариант командной строки для запуска testfile(). Вы можете указать интерпретатору Python запустить модуль doctest непосредственно из стандартной библиотеки и передать имена файла(ей) в командной строке:

python -m doctest -v example.txt

Поскольку имя файла не заканчивается на .py, doctest предполагает, что его нужно запустить с testfile(), а не testmod().

Для получения дополнительной информации о testfile(), см. раздел Основные API.

Как это работает

Этот раздел подробно рассматривает работу doctest: какие строковые документации он анализирует, как находит интерактивные примеры, какой контекст выполнения использует, как обрабатывает исключения и как флаги опций могут использоваться для управления его поведением. Эта информация необходима для написания примеров doctest; для информации о фактическом запуске doctest на этих примерах см. следующие разделы.

Какие строковые документации анализируются?

Ищут строковую документацию модуля и все строковые документации функций, классов и методов. Объекты, импортированные в модуль, не ищутся.

Кроме того, есть случаи, когда вы хотите, чтобы тесты были частью модуля, но не частью справочной информации, что требует, чтобы тесты не включались в строковую документацию. Doctest ищет переменную на уровне модуля, называемую __test__, и использует её для поиска других тестов. Если M.__test__ существует и является истинным, она должна быть словарем, и каждое значение отображает (строковое) имя на объект функции, объект класса или строку. Строковые документации функций и объектов классов, найденные из M.__test__, ищутся, а строки обрабатываются так, как если бы они были строковыми документациями. В выводе ключ K в M.__test__ появляется с именем M.__test__.K.

Например, поместите этот блок кода в верхней части example.py:

__test__ = {
    'numbers': """
>>> factorial(6)
720

>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
"""
}

Значение example.__test__["numbers"] будет обрабатываться как строковая документация, и все тесты внутри неё будут выполняться. Важно отметить, что значение может быть отображено на функцию, объект класса или модуль; в таком случае doctest ищет их рекурсивно для строковых документаций, которые затем сканируются на наличие тестов.

Любые найденные классы рекурсивно ищутся аналогичным образом, для проверки строковых документаций в содержащихся методах и вложенных классах.

Как распознаются примеры в строковых документациях?

В большинстве случаев копирование-вставка сессии интерактивной консоли работает хорошо, но doctest не пытается точно эмулировать какую-либо конкретную оболочку Python.

>>> # comments are ignored
>>> x = 12
>>> x
12
>>> if x == 13:
...     print("yes")
... else:
...     print("no")
...     print("NO")
...     print("NO!!!")
...
no
NO
NO!!!
>>>

Любой ожидаемый вывод должен немедленно следовать за последней строкой кода '>>> ' или '... ', и ожидаемый вывод (если таковой имеется) простирается до следующей пустой строки '>>> ' или до любой строки, состоящей только из пробелов.

Подробности:

  • Ожидаемый вывод не может содержать пустую строку, так как такая строка используется для сигнализации об окончании ожидаемого вывода. Если ожидаемый вывод содержит пустую строку, поместите <BLANKLINE> в ваш пример doctest в каждом месте, где ожидается пустая строка.
  • Все жёсткие символы табуляции расширяются до пробелов, используя 8-колонные отступы. Табуляции в выводе, сгенерированном тестируемым кодом, не изменяются. Поскольку все жёсткие табуляции в образце вывода расширяются, это означает, что если вывод кода включает жёсткие табуляции, единственный способ, которым doctest может пройти, это если опция NORMALIZE_WHITESPACE или директива активна. В качестве альтернативы тест можно переписать, чтобы захватить вывод и сравнить его с ожидаемым значением в рамках теста. Такое обращение с табуляциями в исходном коде было получено путём проб и ошибок и оказалось наименее подверженным ошибкам способом обработки табуляций. Возможно использовать другой алгоритм обработки табуляций, написав собственный класс DocTestParser.
  • Выводится вывод в стандартный вывод, но не в стандартный вывод ошибок (обработка исключений захватывается иным способом).
  • Если вы продолжаете строку с помощью обратного слэша в интерактивной сессии или по любой другой причине используете обратный слэш, вы должны использовать сырую строковую документацию, которая сохранит ваши обратные слэши точно так, как вы их набираете:

    >>> def f(x):
    ...     r'''Backslashes in a raw docstring: m\n'''
    >>> print(f.__doc__)
    Backslashes in a raw docstring: m\n
    

    В противном случае обратный слэш будет интерпретироваться как часть строки. Например, \n выше будет интерпретироваться как символ новой строки. В качестве альтернативы, вы можете удвоить каждый обратный слэш в версии doctest (и не использовать сырую строку):

    >>> def f(x):
    ...     '''Backslashes in a raw docstring: m\\n'''
    >>> print(f.__doc__)
    Backslashes in a raw docstring: m\n
    
  • Начальная колонка не имеет значения:

    >>> assert "Easy!"
          >>> import math
              >>> math.floor(1.9)
              1
    

    и столько же ведущих символов пробелов удаляется из ожидаемого вывода, сколько было в исходной строке '>>> ' , которая начала пример.

Какой контекст выполнения?

По умолчанию каждый раз, когда doctest находит строковую документацию для тестирования, он использует поверхностную копию глобальных переменных M, чтобы запуск тестов не изменял реальные глобальные переменные модуля, и чтобы один тест в M не оставлял следов, которые случайно позволяли бы работать другому тесту. Это означает, что примеры могут свободно использовать любые имена, определённые на верхнем уровне в M, и имена, определённые ранее в выполняемой строковой документации. Примеры не могут видеть имена, определённые в других строковых документациях.

Вы можете принудительно использовать свой собственный словарь в качестве контекста выполнения, передав globs=your_dict в testmod() или testfile() вместо этого.

Что насчёт исключений?

Без проблем, при условии, что трассировка стека — это единственный вывод, произведённый примером: просто вставьте трассировку стека. 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 not in list как показано.

Ожидаемый вывод для исключения должен начинаться с заголовка трассировки стека, который может быть любой из следующих двух строк, отступы которых совпадают с первым отступом примера:

Traceback (most recent call last):
Traceback (innermost last):

Заголовок трассировки стека сопровождается необязательным стеком трассировки, содержимое которого игнорируется doctest. Стек трассировки обычно опускается или копируется дословно из интерактивной сессии.

Стек трассировки следует за наиболее интересной частью: строкой или строками, содержащими тип исключения и его деталь. Это обычно последняя строка трассировки стека, но может охватывать несколько строк, если исключение имеет многострочную деталь:

>>> raise ValueError('multi\n    line\ndetail')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: multi
    line
detail

Последние три строки (начиная с ValueError) сравниваются с типом и деталью исключения, а остальные игнорируются.

Лучшей практикой является опускание стека трассировки, если это не добавляет существенной документационной ценности в пример. Поэтому последний пример, вероятно, лучше в виде:

>>> raise ValueError('multi\n    line\ndetail')
Traceback (most recent call last):
    ...
ValueError: multi
    line
detail

Обратите внимание, что трассировки стека обрабатываются очень особенно. В частности, в переписанном примере использование ... не зависит от опции doctest ELLIPSIS. Многоточие в этом примере можно опустить, или заменить тремя (или тремя сотнями) запятыми или цифрами, или вставленным отступом фрагментом из фильма «Монти Пайтон».

Некоторые детали, которые вы должны прочитать один раз, но вам не нужно запоминать:

  • Doctest не может догадаться, возник ли ожидаемый вывод от трассировки исключения или от обычной печати. Таким образом, например, пример, ожидающий ValueError: 42 is prime , пройдёт, независимо от того, действительно ли возникло ValueError, или пример просто печатает этот текст трассировки стека. На практике обычный вывод редко начинается с строки заголовка трассировки стека, поэтому это не создаёт реальных проблем.
  • Каждая строка стека трассировки (если он присутствует) должна быть с отступом больше, чем первая строка примера, или начинаться с неалфавитно-цифрового символа. Первая строка, идущая после заголовка трассировки стека с таким же отступом и начинающаяся с алфавитно-цифрового символа, рассматривается как начало детали исключения. Конечно, это делает правильную вещь для реальных трассировок стека.
  • Когда опция doctest IGNORE_EXCEPTION_DETAIL указана, всё, что следует за левым двоеточием и любой информацией о модуле в имени исключения, игнорируется.
  • Интерактивная оболочка опускает строку заголовка трассировки стека для некоторых SyntaxError. Но doctest использует строку заголовка трассировки стека, чтобы отличать исключения от неисключений. Поэтому в редких случаях, когда вам нужно протестировать SyntaxError, который опускает строку заголовка трассировки стека, вам нужно будет вручную добавить строку заголовка трассировки стека в ваш пример теста.
  • Для некоторых исключений Python отображает позицию ошибки с помощью маркеров ^ и тильд:

    >>> 1 + None
      File "<stdin>", line 1
        1 + None
        ~~^~~~~~
    TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
    

    Поскольку строки, показывающие позицию ошибки, идут перед типом и деталью исключения, они не проверяются doctest. Например, следующий тест пройдёт, даже если он помещает маркер ^ в неправильное место:

    >>> 1 + None
      File "<stdin>", line 1
        1 + None
        ^~~~~~~~
    TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
    

Флаги параметров

Несколько флагов параметров контролируют различные аспекты поведения doctest. Символьные имена флагов предоставляются в виде констант модуля, которые могут быть побитово объединены и переданы различным функциям. Эти имена также могут быть использованы в директивах doctest и могут быть переданы в командную строку doctest через параметр -o.

Новое в версии 3.4: Параметр командной строки -o.

Первая группа параметров определяет семантику тестирования, управляя аспектами того, как doctest определяет, соответствует ли фактический вывод ожидаемому выводу примера:

doctest.DONT_ACCEPT_TRUE_FOR_1

По умолчанию, если блок ожидаемого вывода содержит только 1, то блок фактического вывода, содержащий только 1 или только True, считается соответствием, и аналогично для 0 по отношению к False. Когда указан DONT_ACCEPT_TRUE_FOR_1, ни одна из этих подстановок не разрешается. По умолчанию поддерживается изменение Python, в котором тип возвращаемого значения многих функций из целого числа изменился на булево; doctest, ожидающие целые числа в выводе, по-прежнему работают в этих случаях. Этот параметр, вероятно, будет удалён, но не в ближайшие несколько лет.

doctest.DONT_ACCEPT_BLANKLINE

По умолчанию, если блок ожидаемого вывода содержит строку, содержащую только строку <BLANKLINE>, то эта строка будет соответствовать пустой строке во фактическом выводе. Поскольку фактически пустая строка ограничивает ожидаемый вывод, это единственный способ указать, что ожидается пустая строка. Когда указан DONT_ACCEPT_BLANKLINE, эта замена не разрешается.

doctest.NORMALIZE_WHITESPACE

При указании все последовательности пробелов (пробелы и новые строки) рассматриваются как равные. Любая последовательность пробелов в ожидаемом выводе будет соответствовать любой последовательности пробелов в фактическом выводе. По умолчанию пробелы должны точно совпадать. NORMALIZE_WHITESPACE особенно полезен, когда строка ожидаемого вывода очень длинная, и вы хотите разбить её на несколько строк в исходном коде.

doctest.ELLIPSIS

При указании маркер многоточия (...) в ожидаемом выводе может соответствовать любому подстроке в фактическом выводе. Это включает подстроки, которые охватывают границы строк, и пустые подстроки, поэтому лучше всего использовать его просто. Сложные случаи могут привести к тем же проблемам «ой, он сопоставил слишком много!», к которым .* предрасположен в регулярных выражениях.

doctest.IGNORE_EXCEPTION_DETAIL

При указании doctest, ожидающие исключений, проходят, если возникает исключение требуемого типа, даже если детали (сообщение и полное имя исключения) не совпадают.

Например, пример, ожидающий ValueError: 42, пройдет, если фактически возникает исключение ValueError: 3*14, но потерпит неудачу, если, скажем, возникнет TypeError. Он также проигнорирует любое полное имя, включённое перед классом исключения, которое может меняться между реализациями и версиями Python и используемым кодом/библиотеками. Таким образом, все три этих варианта будут работать с заданным флагом:

>>> raise Exception('message')
Traceback (most recent call last):
Exception: message

>>> raise Exception('message')
Traceback (most recent call last):
builtins.Exception: message

>>> raise Exception('message')
Traceback (most recent call last):
__main__.Exception: message

Обратите внимание, что ELLIPSIS также может использоваться для игнорирования деталей сообщения об исключении, но такой тест всё ещё может потерпеть неудачу в зависимости от того, присутствует ли имя модуля или точно совпадает.

Изменено в версии 3.2: IGNORE_EXCEPTION_DETAIL теперь также игнорирует любую информацию, относящуюся к модулю, содержащему исключение, которое проверяется.

doctest.SKIP

При указании пример вообще не запускается. Это может быть полезно в контекстах, где примеры doctest служат как документацией, так и тестовыми случаями, и пример должен быть включён для целей документации, но не должен проверяться. Например, вывод примера может быть случайным; или пример может зависеть от ресурсов, которые недоступны драйверу теста.

Флаг SKIP также может использоваться для временного «комментирования» примеров.

doctest.COMPARISON_FLAGS

Маска битов, объединяющая все флаги сравнения выше.

Вторая группа параметров управляет тем, как сообщаются об ошибках тестирования:

doctest.REPORT_UDIFF

При указании ошибки, связанные с ожидаемым и фактическим многострочным выводом, отображаются с помощью унифицированной разницы.

doctest.REPORT_CDIFF

При указании ошибки, связанные с ожидаемым и фактическим многострочным выводом, будут отображаться с помощью контекстной разницы.

doctest.REPORT_NDIFF

При указании различия вычисляются с помощью difflib.Differ, используя тот же алгоритм, что и популярная утилита ndiff.py. Это единственный метод, который отмечает различия как внутри строк, так и между ними. Например, если строка ожидаемого вывода содержит цифру 1, а фактический вывод содержит букву l, вставляется строка с каретом, отмечающим позиции несовпадающих столбцов.

doctest.REPORT_ONLY_FIRST_FAILURE

При указании отображается первый неверный пример в каждом doctest, но вывод для всех остальных примеров подавляется. Это предотвратит doctest от отчёта о корректных примерах, которые ломаются из-за предыдущих ошибок; но это также может скрыть неправильные примеры, которые терпят неудачу независимо от первой ошибки. Когда REPORT_ONLY_FIRST_FAILURE задан, остальные примеры всё ещё запускаются и всё ещё учитываются в общем количестве ошибок, сообщённых; только вывод подавляется.

doctest.FAIL_FAST

При указании выполнение останавливается после первого неверного примера, и попытка запустить оставшиеся примеры не предпринимается. Таким образом, количество ошибок, сообщаемых, не может превышать 1. Этот флаг может быть полезен во время отладки, так как примеры после первой ошибки даже не будут генерировать вывод отладки.

Командная строка doctest принимает параметр -f в качестве сокращения для -o FAIL_FAST.

Новое в версии 3.4.

doctest.REPORTING_FLAGS

Маска битов, объединяющая все флаги отчёта выше.

Также существует способ регистрации новых имён флагов параметров, хотя это не полезно, если вы не намерены расширять 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, который должен быть достаточным для большинства основных случаев использования. Для менее формального введения в эти две функции см. разделы Простое использование: проверка примеров в строках документации и Простое использование: проверка примеров в текстовом файле.

doctest.testfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, parser=DocTestParser(), encoding=None)

Все аргументы, кроме filename, необязательны и должны быть указаны в виде ключевых слов.

Проверить примеры в файле с именем filename. Вернуть (failure_count, test_count).

Необязательный аргумент module_relative определяет, как следует интерпретировать имя файла:

  • Если module_relative равен True (по умолчанию), то filename указывает независимый от ОС путь, относительный к модулю. По умолчанию этот путь относительный к каталогу вызывающего модуля; но если указан аргумент package, то он относительный к этому пакету. Для обеспечения независимости от ОС в filename должны использоваться символы / для разделения сегментов пути, и это не должен быть абсолютный путь (т.е., он не может начинаться с /).
  • Если module_relative равен False, то filename указывает путь, специфичный для ОС. Путь может быть абсолютным или относительным; относительные пути разрешаются относительно текущего рабочего каталога.

Необязательный аргумент name задаёт имя теста; по умолчанию или если None, используется os.path.basename(filename).

Необязательный аргумент package — это Python-пакет или имя Python-пакета, каталог которого следует использовать в качестве базового каталога для имени файла, относительного к модулю. Если пакет не указан, то в качестве базового каталога для имён файлов, относительных к модулю, используется каталог вызывающего модуля. Ошибка возникает при указании package, если module_relative равен False.

Необязательный аргумент globs задаёт словарь, используемый в качестве глобальных переменных при выполнении примеров. Для doctest создаётся новая неглубокая копия этого словаря, поэтому его примеры начинаются с чистого листа. По умолчанию или если None, используется новый пустой словарь.

Необязательный аргумент extraglobs задаёт словарь, который добавляется в глобальные переменные, используемые для выполнения примеров. Это работает аналогично dict.update(): если в globs и extraglobs есть общие ключи, соответствующее значение в extraglobs появляется в объединённом словаре. По умолчанию или если None, дополнительные глобальные переменные не используются. Это расширенная функция, позволяющая параметризовать doctest. Например, doctest можно написать для базового класса, используя общее имя для класса, а затем повторно использовать его для тестирования любого количества подклассов, передавая словарь extraglobs, сопоставляющий общее имя с подклассом, который необходимо протестировать.

Необязательный аргумент verbose выводит много информации, если true, и выводит только ошибки, если false; по умолчанию или если None, это true тогда и только тогда, когда '-v' находится в sys.argv.

Необязательный аргумент report выводит сводку в конце, если true, в противном случае ничего не выводит. В режиме verbose сводка подробная, иначе сводка очень краткая (на самом деле пустая, если все тесты прошли).

Необязательный аргумент optionflags (значение по умолчанию 0) принимает побитовое ИЛИ флагов опций. См. раздел Флаги опций.

Необязательный аргумент raise_on_error по умолчанию равен false. Если true, исключение генерируется при первой ошибке или непредвиденном исключении в примере. Это позволяет выполнить пошаговую отладку ошибок. По умолчанию выполнение примеров продолжается.

Необязательный аргумент parser указывает DocTestParser (или подкласс), который должен использоваться для извлечения тестов из файлов. По умолчанию используется обычный парсер (т.е., DocTestParser()).

Необязательный аргумент encoding указывает кодировку, которая должна использоваться для преобразования файла в юникод.

doctest.testmod(m=None, name=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, exclude_empty=False)

Все аргументы необязательны, и все, кроме m, должны быть указаны в виде ключевых слов.

Тестирование примеров в строках документации в функциях и классах, доступных из модуля m (или модуля __main__, если m не указан или равен None), начиная с m.__doc__.

Также проверяются примеры, доступные из словаря m.__test__, если он существует и не None. m.__test__ отображает имена (строки) на функции, классы и строки; строки документации функций и классов проверяются на наличие примеров; строки проверяются непосредственно, как если бы они были строками документации.

Только строки документации объектов, принадлежащих модулю m, проверяются.

Вернуть (failure_count, test_count).

Необязательный аргумент name задаёт имя модуля; по умолчанию или если None, используется m.__name__.

Необязательный аргумент exclude_empty по умолчанию равен false. Если true, объекты, для которых не найдены тесты doctest, исключаются из рассмотрения. По умолчанию это откат назад для совместимости, чтобы код, все ещё использующий doctest.master.summarize в сочетании с testmod(), продолжал получать вывод для объектов без тестов. Аргумент exclude_empty для нового конструктора DocTestFinder по умолчанию равен true.

Необязательные аргументы extraglobs, verbose, report, optionflags, raise_on_error и globs такие же, как для функции testfile() выше, за исключением того, что globs по умолчанию равен m.__dict__.

doctest.run_docstring_examples(f, globs, verbose=False, name='NoName', compileflags=None, optionflags=0)

Проверить примеры, связанные с объектом f; например, f может быть строкой, модулем, функцией или объектом класса.

Для контекста выполнения используется неглубокая копия словаря-аргумента globs.

Необязательный аргумент name используется в сообщениях об ошибках и по умолчанию равен "NoName".

Если необязательный аргумент verbose равен true, вывод генерируется даже если нет ошибок. По умолчанию вывод генерируется только в случае ошибки примера.

Необязательный аргумент compileflags задаёт набор флагов, которые должен использовать компилятор Python при выполнении примеров. По умолчанию или если None, флаги вычисляются на основе набора будущих возможностей, найденных в globs.

Необязательный аргумент optionflags работает так же, как для функции testfile() выше.

END_OF_DOCUMENT_MARKER

API модуля unittest

По мере роста вашей коллекции модулей с doctest’ами, вам потребуется способ систематического запуска всех их doctest’ов. doctest предоставляет две функции, которые могут использоваться для создания unittest наборов тестов из модулей и текстовых файлов, содержащих doctest’ы. Для интеграции с механизмом обнаружения тестов unittest, включите функцию load_tests в ваш модуль тестов:

import unittest
import doctest
import my_module_with_doctests

def load_tests(loader, tests, ignore):
    tests.addTests(doctest.DocTestSuite(my_module_with_doctests))
    return tests

Существует две основные функции для создания экземпляров unittest.TestSuite из текстовых файлов и модулей с doctest’ами:

doctest.DocFileSuite(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None)

Преобразует doctest-тесты из одного или нескольких текстовых файлов в unittest.TestSuite.

Возвращаемый unittest.TestSuite должен быть запущен фреймворком unittest и выполнит интерактивные примеры в каждом файле. Если пример в любом файле завершится неудачей, то синтезированный тест завершится неудачей, и будет поднято исключение failureException, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.

Передайте один или несколько путей (как строки) к текстовым файлам для проверки.

Параметры могут быть предоставлены в качестве именованных аргументов:

Необязательный аргумент module_relative определяет, как имена файлов в paths должны интерпретироваться:

  • Если module_relative имеет значение True (по умолчанию), то каждое имя файла в paths определяет независимый от ОС путь относительно модуля. По умолчанию этот путь относительный к каталогу вызывающего модуля; но если указан аргумент package, то он относительный к этому пакету. Для обеспечения независимости от ОС каждое имя файла должно использовать / символы для разделения сегментов пути и не может быть абсолютным путем (т.е., не может начинаться с /).
  • Если module_relative равно False, то каждое имя файла в paths определяет путь, зависящий от ОС. Путь может быть абсолютным или относительным; относительные пути разрешаются относительно текущей рабочей директории.

Необязательный аргумент package — это Python-пакет или имя Python-пакета, каталог которого должен использоваться в качестве базового каталога для имен файлов, относительных к модулю, в paths. Если пакет не указан, то каталог вызывающего модуля используется в качестве базового каталога для имен файлов, относительных к модулю. Указание package при module_relative равном False является ошибкой.

Необязательный аргумент setUp определяет функцию настройки для набора тестов. Она вызывается перед запуском тестов в каждом файле. Функция setUp получит объект DocTest. Функция setUp может получить доступ к глобальным переменным теста как атрибуту globs переданного теста.

Необязательный аргумент tearDown определяет функцию завершения для набора тестов. Она вызывается после выполнения тестов в каждом файле. Функция tearDown получит объект DocTest. Функция setUp может получить доступ к глобальным переменным теста как атрибуту globs переданного теста.

Необязательный аргумент globs — словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию, globs — новый пустой словарь.

Необязательный аргумент optionflags задает стандартные опции doctest для тестов, созданные путем объединения отдельных опций флагов. См. раздел Флаги опций. См. функцию set_unittest_reportflags() ниже для лучшего способа настройки опций отчета.

Необязательный аргумент parser задает DocTestParser (или подкласс), который должен использоваться для извлечения тестов из файлов. По умолчанию используется стандартный парсер (т.е., DocTestParser()).

Необязательный аргумент encoding задает кодировку для преобразования файла в Unicode.

Глобальная переменная __file__ добавляется к глобальным переменным, предоставляемым doctest'ам, загружаемым из текстового файла с помощью DocFileSuite().

doctest.DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None)

Преобразует doctest-тесты для модуля в unittest.TestSuite.

Возвращаемый unittest.TestSuite должен быть запущен фреймворком unittest и выполняет каждый doctest в модуле. Если какой-либо doctest завершится неудачей, то синтезированный тест завершится неудачей, и будет поднято исключение failureException, показывающее имя файла, содержащего тест, и (иногда приблизительный) номер строки.

Необязательный аргумент module предоставляет модуль для тестирования. Это может быть объект модуля или (возможно, с точками) имя модуля. Если не указано, используется модуль, вызывающий эту функцию.

Необязательный аргумент globs — словарь, содержащий начальные глобальные переменные для тестов. Для каждого теста создается новая копия этого словаря. По умолчанию, globs — новый пустой словарь.

Необязательный аргумент extraglobs указывает дополнительный набор глобальных переменных, который объединяется с globs. По умолчанию дополнительные глобальные переменные не используются.

Необязательный аргумент test_finder — объект DocTestFinder (или его замена), который используется для извлечения doctest'ов из модуля.

Необязательные аргументы setUp, tearDown и optionflags такие же, как и для функции DocFileSuite() выше.

Эта функция использует ту же технику поиска, что и testmod().

Изменено в версии 3.5: DocTestSuite() возвращает пустой unittest.TestSuite, если module не содержит docstring'ов, вместо того чтобы генерировать ValueError.

exception doctest.failureException

Когда doctest’ы, преобразованные в тесты unittest с помощью DocFileSuite() или DocTestSuite(), завершаются неудачно, это исключение генерируется, показывая имя файла, содержащего тест, и (иногда приблизительный) номер строки.

Внутри DocTestSuite() создает unittest.TestSuite из экземпляров doctest.DocTestCase, а DocTestCase является подклассом unittest.TestCase. DocTestCase здесь не документируется (это внутренняя деталь), но изучение его кода может ответить на вопросы о точных деталях интеграции с unittest.

Аналогично, DocFileSuite() создает unittest.TestSuite из экземпляров doctest.DocFileCase, а DocFileCase является подклассом DocTestCase.

Таким образом, оба способа создания unittest.TestSuite запускают экземпляры DocTestCase. Это важно по одной причине: когда вы запускаете функции doctest самостоятельно, вы можете контролировать используемые опции doctest, напрямую передавая флаги опций функциям doctest. Однако, если вы создаете фреймворк unittest, фреймворк unittest в конечном итоге управляет тем, когда и как запускаются тесты. Автор фреймворка обычно хочет контролировать опции отчета doctest (возможно, заданные, например, опциями командной строки), но нет способа передать опции через unittest в исполнители тестов doctest.

По этой причине, doctest также поддерживает понятие doctest флагов отчётности, специфичных для unittest поддержки, через эту функцию:

doctest.set_unittest_reportflags(flags)

Установить doctest флаги отчётности для использования.

Аргумент flags принимает поразрядное ИЛИ флагов опций. Смотрите раздел Флаги опций. Можно использовать только «флаги отчётности».

Это глобальная настройка модуля, которая влияет на все будущие тесты doctest, выполняемые модулем unittest: метод runTest() объекта DocTestCase рассматривает флаги опций, указанные для тестового случая, когда был создан экземпляр DocTestCase. Если не было указано никаких флагов отчётности (что является типичным и ожидаемым случаем), флаги отчётности unittest объекта doctest выполняются поразрядным ИЛИ с флагами опций, а дополненные таким образом флаги опций передаются в экземпляр DocTestRunner, созданный для запуска doctest. Если при создании экземпляра DocTestCase были указаны какие-либо флаги отчётности, флаги отчётности unittest объекта doctest игнорируются.

Значение unittest флагов отчётности, действующих до вызова функции, возвращается функцией.

Расширенный API

Базовый API — это простой обертка, предназначенный для удобного использования doctest. Он достаточно гибкий и должен удовлетворить потребности большинства пользователей; однако, если вам требуется более тонкий контроль над тестированием или вы хотите расширить возможности doctest, вы должны использовать расширенный API.

Расширенный API вращается вокруг двух контейнерных классов, которые используются для хранения интерактивных примеров, извлеченных из тестов doctest:

  • Example: Один Python-оператор, вместе с ожидаемым результатом.
  • DocTest: Коллекция объектов Example, обычно извлеченных из одного docstring или текстового файла.

Для поиска, разбора, запуска и проверки примеров doctest определены дополнительные классы обработки:

  • DocTestFinder: Находит все docstrings в данном модуле и использует DocTestParser для создания объекта DocTest из каждого docstring, содержащего интерактивные примеры.
  • DocTestParser: Создает объект DocTest из строки (например, docstring объекта).
  • DocTestRunner: Выполняет примеры в объекте DocTest и использует OutputChecker для проверки их результатов.
  • OutputChecker: Сравнивает фактический результат из примера doctest с ожидаемым результатом и определяет, совпадают ли они.

Взаимосвязи между этими классами обработки показаны на следующей диаграмме:

                            list of:
+------+                   +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+    |        ^     +---------+     |       ^    (printed)
            |        |     | Example |     |       |
            v        |     |   ...   |     v       |
           DocTestParser   | Example |   OutputChecker
                           +---------+

Объекты DocTest

class doctest.DocTest(examples, globs, name, filename, lineno, docstring)

Коллекция примеров doctest, которые должны выполняться в одном пространстве имен. Аргументы конструктора используются для инициализации атрибутов с одинаковыми именами.

DocTest определяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.

examples

Список объектов Example, кодирующих отдельные интерактивные Python-примеры, которые должны быть выполнены этим тестом.

globs

Пространство имен (также известное как глобальные переменные), в котором должны выполняться примеры. Это словарь, сопоставляющий имена со значениями. Любые изменения в пространстве имен, произведенные примерами (например, привязка новых переменных), будут отражены в globs после выполнения теста.

name

Строковое имя, идентифицирующее DocTest. Как правило, это имя объекта или файла, из которого был извлечен тест.

filename

Имя файла, из которого был извлечен данный DocTest; или None если имя файла неизвестно или DocTest не был извлечен из файла.

lineno

Номер строки в filename, где начинается этот DocTest, или None если номер строки недоступен. Этот номер строки является нулевым относительно начала файла.

docstring

Строка, из которой был извлечен тест, или None если строка недоступна или тест не был извлечен из строки.

Объекты Example

class doctest.Example(source, want, exc_msg=None, lineno=0, indent=0, options=None)

Один интерактивный пример, состоящий из Python-оператора и ожидаемого результата. Аргументы конструктора используются для инициализации атрибутов с одинаковыми именами.

Example определяет следующие атрибуты. Они инициализируются конструктором и не должны изменяться напрямую.

source

Строка, содержащая исходный код примера. Этот исходный код состоит из одного Python-оператора и всегда заканчивается новой строкой; конструктор добавляет новую строку при необходимости.

want

Ожидаемый результат выполнения исходного кода примера (либо из stdout, либо traceback в случае исключения). want заканчивается новой строкой, если ожидаемый вывод отсутствует, в противном случае это пустая строка. Конструктор добавляет новую строку при необходимости.

exc_msg

Сообщение об исключении, сгенерированное примером, если пример ожидается, что сгенерирует исключение; или None если это не ожидается. Это сообщение об исключении сравнивается со значением, возвращаемым функцией traceback.format_exception_only(). exc_msg заканчивается новой строкой, если это не None. Конструктор добавляет новую строку, если необходимо.

lineno

Номер строки в строке, содержащей этот пример, где начинается пример. Этот номер строки является нулевым относительно начала содержащей строки.

indent

Отступ примера в содержащей строке, т.е., количество пробелов, которые предшествуют первой подсказке примера.

options

Словарь, сопоставляющий флаги опций с True или False, который используется для переопределения стандартных опций для данного примера. Любые флаги опций, которые не содержатся в этом словаре, сохраняются в их стандартном значении (как задано параметром DocTestRunner optionflags). По умолчанию опции не заданы.

Объекты DocTestFinder

class doctest.DocTestFinder(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)

Класс обработки, используемый для извлечения DocTest объектов, относящихся к данному объекту, из его строки документации и строк документации его вложенных объектов. DocTest объекты могут быть извлечены из модулей, классов, функций, методов, статических методов, методов класса и свойств.

Необязательный аргумент 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 из объектов, импортированных из других модулей. (Вложенные объекты с модулями, отличными от module, игнорируются.)
  • Для определения имени файла, содержащего объект.
  • Для определения номера строки объекта в файле.

Если module равен False, попытка найти модуль не будет предпринята. Это используется в основном для тестирования самого doctest: если module равен False, или равен None, но не может быть найден автоматически, все объекты считаются принадлежащими несуществующему модулю, поэтому все вложенные объекты будут (рекурсивно) проверяться на наличие тестов докстринга.

Глобальные переменные для каждого DocTest формируются путём объединения globs и extraglobs (связывания в extraglobs перекрывают связывания в globs). Для каждого DocTest создаётся новая неглубокая копия словаря глобальных переменных. Если globs не указано, оно по умолчанию равно __dict__ модуля, если указано, или {} в противном случае. Если extraglobs не указано, оно по умолчанию равно {}.

Объекты DocTestParser

class doctest.DocTestParser

Класс обработки, используемый для извлечения интерактивных примеров из строки и использования их для создания объекта DocTest.

DocTestParser определяет следующие методы:

get_doctest(string, globs, name, filename, lineno)

Извлекает все примеры doctest из данной строки и собирает их в объект DocTest.

globs, name, filename и lineno — атрибуты нового объекта DocTest. Подробнее см. документацию для DocTest.

get_examples(string, name='<string>')

Извлекает все примеры doctest из данной строки и возвращает их в виде списка объектов Example. Номера строк — от 0. Необязательный аргумент name — имя, идентифицирующее эту строку, используется только для сообщений об ошибках.

parse(string, name='<string>')

Разбивает заданную строку на примеры и промежуточный текст и возвращает их в виде списка, чередующего Example и строк. Номера строк для Example — от 0. Необязательный аргумент name — имя, идентифицирующее эту строку, используется только для сообщений об ошибках.

Объекты DocTestRunner

class doctest.DocTestRunner(checker=None, verbose=None, optionflags=0)

Класс обработки, используемый для выполнения и проверки интерактивных примеров в DocTest.

Сравнение ожидаемых и фактических результатов выполняется с помощью OutputChecker. Это сравнение можно настроить с помощью ряда флагов опций; см. раздел Флаги опций для получения дополнительной информации. Если флагов опций недостаточно, сравнение также можно настроить, передав подкласс OutputChecker в конструктор.

Вывод тестового исполнителя можно контролировать двумя способами. Во-первых, можно передать функцию вывода в TestRunner.run(); эта функция будет вызываться со строками, которые должны быть отображены. По умолчанию используется sys.stdout.write. Если захвата вывода недостаточно, вывод также можно настроить, создав подкласс DocTestRunner и переопределив методы report_start(), report_success(), report_unexpected_exception() и report_failure().

Необязательный ключевой аргумент checker задаёт объект OutputChecker (или его замену), который должен использоваться для сравнения ожидаемого вывода с фактическим выводом примеров doctest.

Необязательный ключевой аргумент verbose контролирует удобочитаемость вывода DocTestRunner. Если verbose имеет значение True, то выводятся сведения о каждом примере по мере его выполнения. Если verbose равно False, то выводятся только результаты неудач. Если verbose не указано или равно None, то подробный вывод используется только при использовании командной строки -v.

Необязательный ключевой аргумент optionflags может использоваться для управления тем, как тестовый исполнитель сравнивает ожидаемый вывод с фактическим выводом и как отображает неудачи. Более подробная информация приведена в разделе Флаги опций.

DocTestRunner определяет следующие методы:

report_start(out, test, example)

Сообщает, что тестовый исполнитель собирается обработать данный пример. Этот метод предоставлен для того, чтобы подклассы DocTestRunner могли настроить свой вывод; его не следует вызывать напрямую.

example — пример, который будет обработан. test — тест, содержащий example. out — функция вывода, переданная в DocTestRunner.run().

report_success(out, test, example, got)

Сообщает, что данный пример был выполнен успешно. Этот метод предоставлен для того, чтобы подклассы DocTestRunner могли настроить свой вывод; его не следует вызывать напрямую.

example — пример, который будет обработан. got — фактический вывод примера. test — тест, содержащий example. out — функция вывода, переданная в DocTestRunner.run().

report_failure(out, test, example, got)

Сообщает, что данный пример завершился ошибкой. Этот метод предоставлен для того, чтобы подклассы DocTestRunner могли настроить свой вывод; его не следует вызывать напрямую.

example — пример, который будет обработан. got — фактический вывод примера. test — тест, содержащий example. out — функция вывода, переданная в DocTestRunner.run().

report_unexpected_exception(out, test, example, exc_info)

Сообщает, что данный пример вызвал непредвиденное исключение. Этот метод предоставлен для того, чтобы подклассы DocTestRunner могли настроить свой вывод; его не следует вызывать напрямую.

example — пример, который будет обработан. exc_info — кортеж, содержащий информацию о непредвиденном исключении (как возвращается sys.exc_info()). test — тест, содержащий example. out — функция вывода, переданная в DocTestRunner.run().

run(test, compileflags=None, out=None, clear_globs=True)

Запустить примеры в test (объект DocTest) и отобразить результаты с помощью функции out.

Примеры выполняются в пространстве имён test.globs. Если clear_globs имеет значение True (по умолчанию), то это пространство имён будет очищено после завершения теста, что поможет при сборе мусора. Если вы хотите проверить пространство имён после завершения теста, используйте clear_globs=False.

compileflags определяет набор флагов, которые должны быть использованы компилятором Python при выполнении примеров. Если не указано, по умолчанию используется набор флагов future-import, применимых к globs.

Вывод каждого примера проверяется с помощью проверяющего вывода DocTestRunner, а результаты форматируются методами DocTestRunner.report_*().

summarize(verbose=None)

Вывести сводку всех тестов, выполненных этим DocTestRunner, и вернуть кортеж TestResults(failed, attempted).

Необязательный аргумент verbose контролирует детализацию сводки. Если удобочитаемость не указана, используется удобочитаемость DocTestRunner.

Объекты OutputChecker

class doctest.OutputChecker

Класс, используемый для проверки того, соответствует ли фактический вывод примера doctest ожидаемому выводу. OutputChecker определяет два метода: check_output(), который сравнивает пару выводов и возвращает True если они совпадают; и output_difference(), который возвращает строку, описывающую различия между двумя выводами.

OutputChecker определяет следующие методы:

check_output(want, got, optionflags)

Возвращает True если фактический вывод примера (got) соответствует ожидаемому выводу (want). Эти строки всегда считаются совпадающими, если они идентичны; но в зависимости от флагов опций, используемых тестовым исполнителем, возможны и другие типы неточного соответствия. Подробнее о флагах опций см. в разделе Флаги опций.

output_difference(example, got, optionflags)

Возвращает строку, описывающую различия между ожидаемым выводом для данного примера (example) и фактическим выводом (got). optionflags — набор флагов опций, используемых для сравнения want и got.

Отладка

Doctest предоставляет несколько механизмов для отладки примеров doctest:

  • Несколько функций преобразуют doctest в исполняемые программы Python, которые можно запустить с помощью отладчика Python, pdb.
  • Класс DebugRunner является подклассом класса DocTestRunner, который генерирует исключение для первого неверного примера, содержащего информацию об этом примере. Эта информация может быть использована для постобработки отладки примера.
  • Примеры unittest, сгенерированные с помощью DocTestSuite(), поддерживают метод debug(), определённый в unittest.TestCase.
  • Вы можете добавить вызов pdb.set_trace() в пример doctest, и вы попадете в отладчик Python, когда эта строка будет выполнена. Затем вы можете просмотреть текущие значения переменных и так далее. Например, предположим, что a.py содержит только этот документ модуля:

    """
    >>> def f(x):
    ...     g(x*2)
    >>> def g(x):
    ...     print(x+3)
    ...     import pdb; pdb.set_trace()
    >>> f(3)
    9
    """
    

    Тогда интерактивная сессия Python может выглядеть следующим образом:

    >>> import a, doctest
    >>> doctest.testmod(a)
    --Return--
    > <doctest a[1]>(3)g()->None
    -> import pdb; pdb.set_trace()
    (Pdb) list
      1     def g(x):
      2         print(x+3)
      3  ->     import pdb; pdb.set_trace()
    [EOF]
    (Pdb) p x
    6
    (Pdb) step
    --Return--
    > <doctest a[0]>(2)f()->None
    -> g(x*2)
    (Pdb) list
      1     def f(x):
      2  ->     g(x*2)
    [EOF]
    (Pdb) p x
    3
    (Pdb) step
    --Return--
    > <doctest a[2]>(1)?()->None
    -> f(3)
    (Pdb) cont
    (0, 3)
    >>>
    

Функции, которые преобразуют doctest в код Python и, возможно, запускают сгенерированный код в отладчике:

doctest.script_from_examples(s)

Преобразование текста с примерами в скрипт.

Аргумент s — строка, содержащая примеры doctest. Строка преобразуется в скрипт Python, где примеры doctest в s преобразуются в обычный код, а всё остальное — в комментарии Python. Сгенерированный скрипт возвращается в виде строки. Например,

import doctest
print(doctest.script_from_examples(r"""
    Set x and y to 1 and 2.
    >>> x, y = 1, 2

    Print their sum:
    >>> print(x+y)
    3
"""))

отображает:

# Set x and y to 1 and 2.
x, y = 1, 2
#
# Print their sum:
print(x+y)
# Expected:
## 3

Эта функция используется внутренне другими функциями (см. ниже), но также может быть полезной, когда вы хотите преобразовать интерактивную сессию Python в скрипт Python.

doctest.testsource(module, name)

Преобразование doctest для объекта в скрипт.

Аргумент module — объект модуля или имён модуля с точками, содержащий объект, doctest которого нас интересует. Аргумент name — имя (внутри модуля) объекта с doctest, который нас интересует. Результатом является строка, содержащая документацию объекта, преобразованную в скрипт Python, как описано для script_from_examples() выше. Например, если модуль a.py содержит функцию верхнего уровня f(), то

import a, doctest
print(doctest.testsource(a, "a.f"))

выводит скрипт-версию документации функции f() с doctest, преобразованными в код, а остальное — в комментарии.

doctest.debug(module, name, pm=False)

Отладка doctest для объекта.

Аргументы module и name такие же, как и для функции testsource() выше. Сгенерированный скрипт Python для документации названного объекта записывается в временный файл, а затем этот файл запускается под управлением отладчика Python, pdb.

Для локального и глобального контекста выполнения используется неглубокая копия module.__dict__.

Необязательный аргумент pm управляет использованием постобработки отладки. Если pm имеет истинное значение, файл скрипта выполняется непосредственно, и отладчик вмешивается только в том случае, если скрипт завершается путём поднятия необработанного исключения. Если это произойдёт, тогда выполняется постобработка отладки, с помощью pdb.post_mortem(), передавая объект traceback от необработанного исключения. Если pm не указан или имеет ложное значение, скрипт запускается под отладчиком с самого начала, путём передачи соответствующего вызова exec() к pdb.run().

doctest.debug_src(src, pm=False, globs=None)

Отладка doctest в строке.

Это аналогично функции debug() выше, за исключением того, что строка, содержащая примеры doctest, задаётся непосредственно через аргумент src.

Необязательный аргумент pm имеет то же значение, что и в функции debug() выше.

Необязательный аргумент globs задаёт словарь, используемый как локальный и глобальный контекст выполнения. Если не указан или None, используется пустой словарь. Если указан, используется неглубокая копия словаря.

Класс DebugRunner и особые исключения, которые он может генерировать, представляют наибольший интерес для авторов фреймворков тестирования и будут здесь лишь схематично описаны. Более подробную информацию см. в исходном коде и, особенно, в строке документации DebugRunner (которая является doctest!).

class doctest.DebugRunner(checker=None, verbose=None, optionflags=0)

Подкласс DocTestRunner, который генерирует исключение, как только возникает ошибка. Если возникает непредвиденное исключение, генерируется исключение UnexpectedException, содержащее тест, пример и исходное исключение. Если вывод не совпадает, генерируется исключение DocTestFailure, содержащее тест, пример и фактический вывод.

Подробности о параметрах конструктора и методах см. в документации к DocTestRunner в разделе Расширенный API.

Существует два исключения, которые могут быть сгенерированы экземплярами DebugRunner:

exception doctest.DocTestFailure(test, example, got)

Исключение, генерируемое DocTestRunner для сигнализации о том, что фактический вывод примера doctest не совпал с ожидаемым выводом. Аргументы конструктора используются для инициализации атрибутов с одинаковыми именами.

DocTestFailure определяет следующие атрибуты:

DocTestFailure.test

Объект DocTest, который выполнялся, когда пример завершился ошибкой.

DocTestFailure.example

Пример Example, который завершился ошибкой.

DocTestFailure.got

Фактический вывод примера.

exception doctest.UnexpectedException(test, example, exc_info)

Исключение, генерируемое DocTestRunner для сигнализации о том, что пример doctest вызвал непредвиденное исключение. Аргументы конструктора используются для инициализации атрибутов с одинаковыми именами.

UnexpectedException определяет следующие атрибуты:

UnexpectedException.test

Объект DocTest, который выполнялся, когда пример завершился ошибкой.

UnexpectedException.example

Пример Example, который завершился ошибкой.

UnexpectedException.exc_info

Кортеж, содержащий информацию о непредвиденном исключении, как возвращается sys.exc_info().

END_OF_DOCUMENT_MARKER

Soapbox

Как упоминалось во введении, doctest получил три основных назначения:

  1. Проверка примеров в docstring.
  2. Тестирование регрессии.
  3. Исполняемая документация/литературное тестирование.

Эти назначения имеют разные требования, и важно их различать. В частности, заполнение docstring неочевидными тестовыми случаями приводит к плохой документации.

При написании docstring выбирайте примеры в docstring с умом. В этом есть искусство, которое нужно освоить — оно может не прийти сразу. Примеры должны приносить реальную пользу документации. Хороший пример может стоить многих слов. Если подойти к этому со вниманием, примеры окажутся неоценимыми для ваших пользователей и окупят время, потраченное на их сбор, по мере того, как со временем что-то изменится. Меня до сих пор поражает, как часто один из моих doctest примеров перестаёт работать после «безобидного» изменения.

Doctest также является отличным инструментом для тестирования регрессии, особенно если вы не экономите на пояснительном тексте. Объединяя прозу и примеры, гораздо проще отслеживать, что именно тестируется и почему. Когда тест терпит неудачу, хорошая проза может значительно облегчить выявление проблемы и её решение. Конечно, вы могли бы написать обширные комментарии в кодовом тестировании, но мало кто из программистов так делает. Многие обнаружили, что использование подхода doctest вместо этого приводит к более понятным тестам. Возможно, это просто потому, что doctest делает написание прозы немного проще, чем написание кода, в то время как написание комментариев в коде немного сложнее. Думаю, дело глубже, чем просто это: естественная установка при написании теста на основе doctest — это стремление объяснить тонкие моменты вашего ПО и проиллюстрировать их примерами. Это, в свою очередь, естественным образом приводит к файлам тестов, которые начинаются с самых простых функций и логично переходят к сложностям и граничным случаям. Результатом является связный рассказ, а не набор изолированных функций, которые тестируют изолированные фрагменты функциональности, по-видимому, случайно. Это другой подход, и он приводит к другим результатам, размывая грань между тестированием и объяснением.

Тестирование регрессии лучше всего ограничить специализированными объектами или файлами. Существует несколько вариантов организации тестов:

  • Напишите текстовые файлы, содержащие тестовые случаи в виде интерактивных примеров, и протестируйте файлы с помощью testfile() или DocFileSuite(). Это рекомендуется, хотя проще всего для новых проектов, спроектированных с самого начала для использования doctest.
  • Определите функции с именем _regrtest_topic , состоящие из одного docstring, содержащего тестовые случаи для указанных тем. Эти функции можно включить в тот же файл, что и модуль, или вынести в отдельный тестовый файл.
  • Определите __test__ словарь, сопоставляющий темы тестовых случаев регрессии с docstring, содержащими тестовые случаи.

Когда вы разместили тесты в модуле, сам модуль может стать тестовым исполнителем. Когда тест завершается неудачно, вы можете настроить свой тестовый исполнитель так, чтобы он повторно запускал только неработающий doctest во время отладки проблемы. Вот минимальный пример такого тестового исполнителя:

if __name__ == '__main__':
    import doctest
    flags = doctest.REPORT_NDIFF|doctest.FAIL_FAST
    if len(sys.argv) > 1:
        name = sys.argv[1]
        if name in globals():
            obj = globals()[name]
        else:
            obj = __test__[name]
        doctest.run_docstring_examples(obj, globals(), name=name,
                                       optionflags=flags)
    else:
        fail, total = doctest.testmod(optionflags=flags)
        print("{} failures out of {} tests".format(fail, total))

Примечания

1

Примеры, содержащие как ожидаемый вывод, так и исключение, не поддерживаются. Попытка угадать, где заканчивается один и начинается другой, слишком подвержена ошибкам, а также приводит к запутанному тесту.

© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/doctest.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API