doctest测试交互式Python示例

源代码: Lib / doctest.py


doctest模块搜索看起来像interactivePython会话的文本片段,然后执行这些会话以验证它们如图所示。有几种常用的方法可以使用doctest:

  • 通过验证所有交互式示例是否仍然按照文档记录来检查模块的文档字符串是否是最新的.
  • 通过验证是否执行回归测试来自atest文件或测试对象的交互式示例按预期工作.
  • 为包编写教程文档,自由插图输出示例。根据示例或expository textare强调,这具有“文化测试”或“可执行文档”的味道.

这是一个完整但小的示例模块:

"""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 resultif __name__ == "__main__":    import doctest    doctest.testmod()

如果你运行example.py直接从命令行,doctest发挥其魔力:

$ python example.py$

没有输出!这是正常的,这意味着所有的例子都有效。通过-v到脚本,并且doctest打印出一个详细的日志记录,并在最后打印一个摘要:

$ python example.py -vTrying:    factorial(5)Expecting:    120okTrying:    [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 largeok2 items passed all tests:   1 tests in __main__   8 tests in __main__.factorial9 tests in 2 items.9 passed and 0 failed.Test passed.$

这就是你的全部需要知道开始有效利用doctest!跳入。以下部分提供了完整的细节。请注意,标准Python测试套件和库中有很多doctests示例。特别有用的示例可以在标准测试文件中找到Lib/test/test_doctest.py.

简单用法:检查Docstrings中的示例

最简单的方法开始使用doctest(但不一定是你继续这样做的方式)是结束每个模块M用:

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

doctest然后检查模块中的docstrings M.

运行模块为脚本导致文档字符串中的示例被getexecuted和验证:

python M.py

除非示例失败,否则不会显示任何内容,在这种情况下,失败示例和失败的原因(s)打印到stdout,输出的最终行是***Test Failed*** N failures.,其中N是失败的例子数量

-v转换它来代替:

python M.py -v

所有试用的例子的详细报告都印在标准输出上,最后附有各种摘要.

你可以通过verbose=Truetestmod()或者通过verbose=False来禁止它。在任何一种情况下,sys.argv都没有被testmod()检查(所以传递-v或者没有效果).

还有一个命令行快捷方式跑步testmod()。您可以构造Python解释器直接从标准库运行doctest模块,并在命令行上传递模块名称:

python -m doctest -v example.py

这将导入example.py作为一个独立的模块并在其上运行testmod()。请注意,如果文件是包的一部分并从该包导入其他子模块,这可能无法正常工作.

有关testmod(),请参阅基本API .

简单用法:检查文本文件中的示例

doctest的另一个简单应用是在文本文件中测试交互式示例。这可以通过testfile()函数来完成:

import doctestdoctest.testfile("example.txt")

这个简短的脚本执行并验证文件example.txt中包含的任何交互式Python示例。文件内容被视为是一个巨大的文档字符串;该文件不需要包含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 factorialNow use it:    >>> factorial(6)    120

运行doctest.testfile("example.txt")然后在文档中找到错误:

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

testmod(), testfile()一样不会除非示例失败,否则显示任何内容。如果示例失败,那么失败的示例和失败的原因将打印到stdout,使用与testmod().

相同的格式。默认情况下,testfile()看起来对于调用模块目录中的文件。参见基本API 有关可选参数的说明,可用于告诉它在其他位置查找文件.

喜欢testmod(), testfile()的详细程度可以使用-v命令行开关或可选的关键字参数verbose.

还有一个用于运行testfile()的命令行快捷方式。您可以构造Python解释器直接从标准库运行doctest模块,并在命令行上传递文件名:

python -m doctest -v example.txt

因为文件名不以.py, doctest结尾,所以必须运行testfile(),而不是testmod().

有关testfile()的更多信息,请参阅基本API .

如何使用

本节详细介绍doctest的工作原理:它查看哪些文档字符串,如何查找交互式示例,它使用的执行上下文,如何处理异常,以及如何使用选项标志来控制其行为。这是信息你需要知道写doctest的例子;有关在这些示例中实际运行doctest的信息,请参阅以下章节.

哪些文档字符串被检查?

模块docstring,以及所有函数,类和方法文档字符串。导入模块的对象不被搜索.

另外,如果M.__test__存在且“是真的”,它必须是一个字典,并且eachentry将(字符串)名称映射到函数对象,类对象或字符串。函数和类对象文档字符串从M.__test__搜索,字符串被视为文档字符串。在输出中,KM.__test__出现名称

<name of M>.__test__.K

发现的任何类都是类似的递归搜索,以测试其包含的方法和嵌套类中的文档字符串.

CPython implementation detail:在3.4版之前,doctest编写的扩展模块没有被doctest.

完全搜索。如何识别文档字符串示例

在大多数情况下,交互式控制台会话的复制和粘贴工作正常,但doctest并不试图对任何特定的Python shell进行精确模拟.

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

任何预期的输出必须紧跟在包含代码的最终">>> ""... "行之后,并且预期的输出(如果有的话)延伸到下一个">>> "或全空白行。

小字:

  • 预期的输出不能包含全空行,因为这样的行是用来表示预期输出的结束。如果预期输出确实包含ablank行,则在你的doctest示例中放置<BLANKLINE>每个地方都需要一个空行.

  • 所有硬标签字符都扩展为空格,使用8列制表符停止.Tabs in测试代码生成的输出不会被修改。因为示例输出中的任何硬标签are展开,这意味着如果codeoutput包含硬标签,doctest可以通过的唯一方法是NORMALIZE_WHITESPACE选项或指令有效。或者,可以重写测试以捕获输出并将其作为测试的一部分与预期值进行比较。这种对资源中标签的处理是通过反复试验得出的,并且已被证明是处理它们的极易出错的方式。通过编写自定义DocTestParser class.

  • 捕获输出到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

    并且从启动示例的初始">>> "行中出现的许多前导空白字符被删除了.

执行上下文是什么?

默认情况下,每次doctest找到要测试的文档字符串,它使用shallow copyM的全局变量,因此运行测试不会改变模块的实际全局变量,所以在中进行一次测试M不能留下不小心允许另一个测试工作的后面的错误。这意味着示例可以自由地使用M中顶级定义的任何名称,以及正在运行的docstring中定义的名称。示例无法看到otherdocstrings中定义的名称.

您可以通过将globs=your_dict传递给testmod()testfile()来强制使用您自己的dict作为执行上下文。

例外怎么办?

没问题,只要回溯是示例产生的唯一输出:只需粘贴回溯即可。[1]由于回溯包含可能快速变化的细节(例如,确切的文件路径和亚麻布),这是一个案例,其中doctest很难灵活地接受它的概念.

简单示例:

>>> [1, 2, 3].remove(42)Traceback (most recent call last):  File "<stdin>", line 1, in <module>ValueError: list.remove(x): x not in list

如果ValueError被提升,那么doctest会成功,list.remove(x):x not in list详细信息如图所示.

异常的预期输出必须以traceback标头开头,它可以是以下两行中的缩进与示例的第一行相同:

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

traceback标头后跟一个可选的回溯堆栈,doctest将忽略其内容。回溯堆栈通常被省略,或者从交互式会话中逐字复制.

回溯堆栈后面是最有趣的部分:包含异常类型和细节的行。这通常是atraceback的最后一行,但如果异常具有多行详细信息,则可以扩展到多行:

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

最后三行(以ValueError开头)与exception的类型和细节进行比较,其余部分被忽略.

最佳做法是省略回溯堆栈,除非它为示例添加了重要的文档值。所以最后一个例子可能更好:

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

请注意,回溯的处理非常特殊。特别是在一个例子中,...的使用与doctestELLIPSIS选项。这个例子中的省略号可以省略,或者也可以是三(或三百)个逗号或数字,或Monty Python短片的缩进字幕.

你应该阅读一些细节,但是赢了’需要记住:

  • Doctest无法猜测您的预期输出是来自异常追踪还是来自普通打印。因此,例如,一个期望ValueError: 42 is prime的例子将通过是否实际调用ValueError或者示例是否仅打印该回溯文本。在实践中,普通输出很少以traceback标题行开头,因此这不会产生实际问题.
  • 回溯堆栈的每一行(如果存在)必须在示例的第一行中进一步缩进,or以非字母数字字符开头。跟踪标题后面的第一行缩进相同,并以字母数字开头,作为异常详细信息的开头。当然,这对正版追溯是正确的.
  • IGNORE_EXCEPTION_DETAIL指定了doctest选项,忽略了最左边冒号后面的所有内容以及异常名称中的任何模块信息.
  • 交互式shell省略了某些SyntaxError s的回溯标题行。但doctest使用traceback标题行来区分异常和异常。所以在极少数情况下你需要测试一个省略了traceback标头的SyntaxError,你需要手动将traceback标题行添加到你的测试例中.
  • 对于某些SyntaxErrors,Python使用^ marker:

    >>> 1 1  File "<stdin>", line 1    1 1      ^SyntaxError: invalid syntax

    显示thesyntax错误的字符位置由于显示错误位置的行位于异常类型和详细信息之前,因此doctest不会检查它们。例如,以下测试将通过,即使它将^标记放在错误的位置:

    >>> 1 1  File "<stdin>", line 1    1 1    ^SyntaxError: invalid syntax

Option Flags

许多选项标志控制doctest行为的各个方面。标志的符号名称作为模块常量提供,可以按位ORed 一起并传递给各种功能。名称也可用于doctest指令,可以通过-o选项。

版本3.4中新增: -o命令行选项.

第一组选项定义测试语义,控制howdoctest的方面决定实际输出是否与示例的预期输出匹配:

doctest.DONT_ACCEPT_TRUE_FOR_1

默认情况下,如果预期的输出块仅包含1,则仅包含1或仅True的实际输出块被认为是匹配的,类似于0False。什么时候 DONT_ACCEPT_TRUE_FOR_1指定,不允许替换。默认行为迎合了thePython将许多函数的返回类型从integer更改为boolean; doctests期望“小整数”输出仍然适用于这些情况。这个选项可能会消失,但不会好几年.

doctest.DONT_ACCEPT_BLANKLINE

默认情况下,如果预期的输出块包含仅包含字符串<BLANKLINE>,那么该行将匹配实际输出中的空行。因为真正的空行界定了预期的输出,所以这是传达预期空行的唯一方法。当指定DONT_ACCEPT_BLANKLINE时,不允许这种替换.

doctest.NORMALIZE_WHITESPACE

当指定时,所有空格序列(空格和换行符)都被视为等同。预期输出中的任何空格序列都将匹配实际输出中的空格的任何序列。默认情况下,空格必须完全匹配。当一行预期的输出非常长时,NORMALIZE_WHITESPACE特别有用,你想把它包装在你的源代码中的多行中

doctest.ELLIPSIS

指定时,省略号标记(...)在预期的输出中可以在实际输出中使用matchany子串。这包括跨越lineboundaries和空子串的子串,所以最好继续使用这个简单的。复杂的用法可以导致相同类型的“oops,它匹配得太多了!”令人惊讶的是.*很容易在正则表达式中

doctest.IGNORE_EXCEPTION_DETAIL

指定时,如果引发预期类型的​​异常,即使异常详细信息不匹配,也会传递一个期望异常的示例。例如,如果提出的假设是ValueError: 42,那么期待ValueError: 3*14的例子将会通过,但是会失败,例如,如果TypeError被提升了

它也会忽略Python 3 doctest报告中使用的模块名称。因此,无论测试是在Python 2.7或Python 3.2(或更高版本)下运行,这些变体都将与指定的标志一起使用:

>>> raise CustomError("message")Traceback (most recent call last):CustomError: message>>> raise CustomError("message")Traceback (most recent call last):my_module.CustomError: message

注意ELLIPSIS也可用于忽略详细信息异常消息,但这样的测试可能仍然失败,因为模块详细信息是否作为异常名称的一部分打印。使用IGNORE_EXCEPTION_DETAIL和Python 2.3中的细节也是编写doctest的唯一明确方法,它不关心异常细节但仍然在Python 2.3之前传递(这些版本不支持doctest指令并将它们视为不相关的评论)。例如:

>>> (1, 2)[3] = "moo"Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: object doesn"t support item assignment

在Python 2.3和更高版本的Python版本中传递指定的标志,即使Python 2.4中的detail更改为“不”而不是“不”.

更改在版本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作为-oFAIL_FAST.

的简写3.4版本中的新功能

doctest.REPORTING_FLAGS

一个位掩码或者将上面的所有报告标志组合在一起.

还有一种方法可以注册新的选项标志名称,尽管除非你打算这样做通过子类化扩展doctest内部:

doctest.register_optionflagname

创建一个具有给定名称的新选项标志,并返回新标志的整数值。register_optionflag()可以在子类化OutputCheckerDocTestRunner时使用,以创建子类支持的新选项。register_optionflag()应始终使用以下习语:

MY_FLAG = register_optionflag("MY_FLAG")

指令

Doctest指令可用于修改选项标志以获取单个示例。Doctest指令是一个示例的源代码后的特殊Python注释:

directive             ::=  "#" "doctest:" directive_optionsdirective_options     ::=  directive_option ("," directive_option)\*directive_option      ::=  on_or_off directive_option_nameon_or_off             ::=  "+" \| "-"directive_option_name ::=  "DONT_ACCEPT_BLANKLINE" \| "NORMALIZE_WHITESPACE" \| ...

+-与指令选项名之间不允许有空格。指令选项名称可以是上面解释的任何选项标志名称.

示例doctest指令修改doctest对该单个示例的行为。使用+启用命名行为,或-禁用它.

例如,此测试通过:

>>> print(list(range(20))) [0,   1,  2,  3,  4,  5,  6,  7,  8,  9,10,  11, 12, 13, 14, 15, 16, 17, 18, 19]

如果没有指令它会失败,两者都是因为实际输出在单位数列表元素之前没有两个空格,并且因为实际输出在一行上。此测试也通过了,并且还需要指令doso:

>>> print(list(range(20))) [0, 1, ..., 18, 19]

可以在单个物理线路上使用多个指令,由逗号分隔:

>>> print(list(range(20))) [0,    1, ...,   18,    19]

如果单个示例使用多个指令注释,则它们被组合:

>>> print(list(range(20))) ...                        [0,    1, ...,   18,    19]

如前面的示例所示,您可以将...行添加到仅包含指令的示例中。当一个示例太长而且指令太舒适地放在同一行上时,这可能很有用:

>>> print(list(range(5)) + list(range(10, 20)) + list(range(30, 40)))... [0, ..., 4, 10, ..., 19, 30, ..., 39]

请注意,由于默认情况下禁用所有选项,并且指令仅适用于它们出现的示例,因此启用选项(通过+在指令中)通常是唯一有意义的选择。但是,选项标志也可以传递运行doctests的功能,建立不同的默认值。在这种情况下,在指令中通过-禁用选项可能很有用.

警告

doctest认真对待要求预期输出的完全匹配。如果单个字符不匹配,则测试失败。这可能会让您感到惊讶几次,因为您正确切地了解Python的作用并且不保证输出。例如,在打印字典时,Python不保证键值对将以任何特定顺序打印,所以最喜欢

>>> foo(){"Hermione": "hippogryph", "Harry": "broomstick"}

很脆弱!一个解决方法是改为

>>> foo() == {"Hermione": "hippogryph", "Harry": "broomstick"}True

。另一个是做

>>> d = sorted(foo().items())>>> d[("Harry", "broomstick"), ("Hermione", "hippogryph")]

还有其他的,但你明白了.

另一个坏主意就是打印嵌入物体地址的东西,比如

>>> id(1.0) # certain to fail some of the time7948648>>> class C: pass>>> C()   # the default repr() for instances embeds an address<__main__.C instance at 0x00AC18F0>

ELLIPSIS指令为最后一个例子提供了一个很好的方法:

>>> C() <__main__.C instance at 0x...>

浮点数也会受到各种平台的小输出变化的影响,因为Python遵循平台C库进行浮点格式化,而C库的质量差别很大.

>>> 1./7  # risky0.14285714285714285>>> print(1./7) # safer0.142857142857>>> print(round(1./7, 6)) # much safer0.142857

表单的数量I/2.**J在所有平台上都是安全的,我经常会使用doctest示例来生成这种形式的数字:

>>> 3./4  # utterly safe0.75

简单的分数也让人们更容易理解,这使得更好的文档.

基本API

功能testmod()testfile()提供一个简单的界面todoctest,足以满足大多数基本用途。有关这两个功能的不太正式的介绍,请参阅简单用法:检查文档字符串中的示例简单用法:检查文本文件中的示例.

doctest.testfilefilename, 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是可选的,应该在keywordform.

中指定名为filename的文件中的测试示例。返回(failure_count,test_count).

可选参数module_relative指定如何解释文件名:

  • 如果module_relativeTrue(默认值),然后filename指定与OS无关的模块相对路径。默认情况下,此路径相对于调用模块的目录;但如果package指定了参数,然后它相对于该包。为了确保操作系统独立性,filename应该使用/用于分隔路径段的字符,可能不是绝对路径(即,它可能不以/开头).
  • 如果module_relativeFalse,然后filename指定一个特定于操作系统的路径。路径可以是绝对的或相对的;相对路径是根据当前工作目录解决的.

可选参数name给出测试的名称;默认情况下,或者如果None,os.path.basename(filename)被使用.

可选参数package是一个Python包或一个Python包的名称,该目录应该用作模块的基本目录-relativefilename。如果未指定包,则调用模块的目录将用作模块相关文件名的基本目录。如果packagemodule_relative这是一个错误:False.

可选参数globs在执行示例时给出一个用作全局变量的字典。这个dict的新浅表副本是为doctest创建的,所以它的示例从一个干净的平板开始。默认情况下,或者如果None,使用新的空白字谜

可选参数extraglobs将dict合并到用于执行示例的全局变量中。这有点像dict.update():如果globsextraglobs有一个公共密钥,extraglobs中的相关值出现在组合字典中。默认情况下,或者None,没有使用额外的全局变量。这是一项高级功能,允许对doctests进行参数化。例如,可以使用类的通用名称为基类编写adoctest,然后通过传递extraglobs将通用名称dictmapping到要测试的子类.

可选参数verbose如果为true则打印很多东西,如果为false则仅打印失败;默认情况下,或者None,当且仅当"-v"sys.argv.

可选参数report如果为true,则在末尾打印摘要,否则在最后打印。在详细模式中,摘要是详细的,否则摘要非常简短(事实上,如果所有测试都通过,则为空).

可选参数optionflags(默认值0)带有按位OR 选项标志。参见选项标志.

可选参数raise_on_error默认为false。如果为true,则在示例中的第一次失败或意外异常时引发异常。这允许对事后调试失败。默认行为是继续运行示例.

可选参数parser指定DocTestParser(或子类),用于从文件中提取测试。默认为普通解析器(即DocTestParser()).

可选参数encoding指定应该使用的编码将文件转换为unicode.

doctest.testmodm=None, name=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, exclude_empty=False

所有参数都是可选的,除了m之外的所有参数都应该以关键字形式指定.

函数和类中文档字符串中的测试示例可以访问来自模块m(或模块__main__如果m未提供或是None),从m.__doc__.

开始也可以从dict m.__test__,如果它存在且不是None. m.__test__将名称(字符串)映射到函数,类和字符串;搜索函数和类文档字符串的示例;直接搜索字符串,好像它们是docstrings一样.

只搜索属于模块m的对象附带的文档字符串.

返回(failure_count, test_count).

可选参数name给出模块的名称;默认情况下,或者如果使用None, m.__name__.

可选参数exclude_empty默认为false。如果为true,则不包括未找到doctests的对象。默认是一个向后兼容性黑客,所以仍然使用doctest.master.summarize()testmod()连接的代码继续获得带有notest的对象的输出。exclude_empty较新的DocTestFinder构造函数的参数默认为true.

可选参数extraglobs, verbose, report, optionflags,raise_on_errorglobs与函数相同testfile()以上,除了globs默认为m.__dict__.

doctest.run_docstring_examplesf, globs, verbose=False, name=”NoName”, compileflags=None, optionflags=0

与对象相关的测试示例f;例如,f可以是字符串,模块,函数或类对象.

字典参数的浅拷贝globs用于执行上下文.

可选参数name用于失败消息,默认为"NoName".

如果是可选的参数verbose为真,即使存在nofailures也会生成输出。默认情况下,仅在示例失败的情况下生成输出.

可选参数compileflags给出了运行示例时Python编译器应该使用的标志集。默认情况下,或None,推导出与globs.

中可见的未来功能集相对应的标志。可选参数optionflags与上面的函数testfile()相同。

Unittest API

当你的doctest’ed模块的集合增长时,你需要一种方法来系统地运行他们所有的doctests。doctest提供了两个函数,可用于从包含doctests的模块和文本文件创建unittest测试套件。要与unittest测试发现集成,请在测试模块中包含load_tests()功能:

import unittestimport doctestimport my_module_with_doctestsdef load_tests(loader, tests, ignore):    tests.addTests(doctest.DocTestSuite(my_module_with_doctests))    return tests

创建unittest.TestSuite实例来自文本文件和带有doctests的模块:

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

doctest测试从一个或多个文本文件转换为unittest.TestSuite.

返回的unittest.TestSuite将由unittest框架运行并在每个文件中运行交互式示例。如果任何文件中的示例失败,则合成单元测试失败,并且failureException引发异常,显示包含测试的文件的名称和(有时是近似的)行号.

将一个或多个路径(作为字符串)传递给要检查的文本文件.

选项可以作为关键字参数提供:

可选参数module_relative指定如何解释paths中的文件名:

  • 如果module_relativeTrue(默认值),然后paths指定与OS无关的模块相对路径。默认情况下,此路径相对于调用模块的目录;但如果package指定了参数,然后它相对于该包。为了保证独立性,每个文件名应该使用/用于分隔路径段的字符,可能不是绝对路径(即,它可能不以/开头).
  • 如果module_relativeFalse,那么paths中的每个文件名指定特定于操作系统的路径。路径可以是绝对的或相对的;相对路径就当前工作目录而言是解决的.

可选参数package是Python包或Python包的名称,其目录应该用作基本目录中的formodule-relative文件名paths。如果未指定包,则将调用模块的目录用作模块相关文件名的基本目录。如果packagemodule_relative则指定False.

是错误的可选参数setUp指定测试套件的设置功能。这在运行前调用每个文件中的测试。setUp函数将传递给DocTest对象。setUp函数可以访问测试全局变量作为测试传递的globs属性

可选参数tearDown指定测试套件的拆卸功能。在每个文件中运行测试后调用此方法。tearDown函数将传递给DocTest对象。setUp函数可以接受测试全局变量作为测试传递的globs属性.

可选参数globs是包含测试初始全局变量的字典。为每个测试创建此词典的新副本。默认情况下,globs是一个新的空字典.

可选参数optionflags指定测试的默认doctest选项,通过组合各个选项标志创建。见选项标志。请参阅下面的函数set_unittest_reportflags()以获得设置报告选项的更好方法.

可选参数parser指定应该用于提取测试的DocTestParser(或子类)从文件。它默认为normalparser(即DocTestParser()).

可选参数encoding指定一个应该用于将文件转换为unicode的编码.

全球__file__添加到使用DocFileSuite().

doctest.DocTestSuitemodule=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None

从文本文件加载到doctests的全局变量中将模块的doctest测试转换为unittest.TestSuite.

返回的unittest.TestSuite将由unittest框架运行并在模块中运行每个doctest。如果任何doctests失败,那么合成单元测试失败,并且会引发failureException异常,显示包含测试的文件的名称和(有时是近似的)行号.

可选参数module提供要测试的模块。它可以是模块对象或(可能是虚线的)模块名称。如果没有指定,则使用调用该函数的模块.

可选参数globs是包含测试的初始全局变量的字典。为每个测试创建此词典的新副本。默认情况下,globs是一个新的空字典.

可选参数extraglobs指定一组额外的全局变量,这些变量合并到globs中。默认情况下,没有使用额外的全局变量.

可选参数test_finderDocTestFinder用于从模块中提取doctests的对象(或adrop-in替换).

可选参数setUp, tearDown,和optionflags与功能相同DocFileSuite()以上。

此函数使用与testmod().

相同的搜索技术在版本3.5中更改:DocTestSuite()返回空unittest.TestSuite如果module不包含文档字符串而不是提升ValueError.

在封面下,DocTestSuite()创建一个unittest.TestSuite outof doctest.DocTestCase实例,而DocTestCaseunittest.TestCase. DocTestCase的子类,不是文献记录(这是一个内部细节),但研究它的代码可以回答关于unittest整合.

同样,DocFileSuite()unittest.TestSuite实例中创建一个doctest.DocFileCaseDocFileCaseDocTestCase.

的子类所以两种方式创建unittest.TestSuite运行DocTestCase。这有一个微妙的原因很重要:当你自己运行doctest函数时,可以通过将选项标志传递给doctest来直接控制doctest功能。但是,如果您正在编写unittest框架,unittest最终会控制测试运行的时间和方式。框架作者通常想控制doctest报告选项(也许,例如,由命令行选项指定),但没有办法通过unittest传递选项doctest测试跑步者

为此,doctest也支持doctest报告特定于unittest支持的标志,通过这个函数:

doctest.set_unittest_reportflagsflags

设置doctest报告标志使用.

参数flags选项标志。见节选项标志。只能使用“报告标志”.

这是一个模块全局设置,影响模块unittestrunTest()的方法 DocTestCase查看DocTestCase实例已构建。如果没有指定报告标志(这是典型的和预期的情况),doctestunittest报告标志是按位ORed 进入选项标志,并将选项flagsso增强传递给DocTestRunner实例创建了torun doctest。如果DocTestCase实例构建,doctestunittest报告标志被忽略.

的价值unittest函数返回函数返回之前生效的报告标志.

Advanced API

基本API是一个简单的包装器,旨在使doctest易于使用。它相当灵活,应该满足大多数用户的需求;但是,如果您需要对测试进行更细粒度的控制,或者希望扩展doctest的功能,那么您应该使用高级API .

高级API围绕两个容器类,它们用于存储从doctest案例中提取的交互式示例

  • Example:单个Python 语句与其预期输出配对.
  • DocTestExample s的集合,通常从单个文档字符串或文本文件中提取.

其他处理类被定义为查找,解析,运行和checkdoctest示例:

  • 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 Objects

class doctest.DocTestexamples, globs, name, filename, lineno, docstring

应该在单个命名空间中运行的doctest示例集合。构造函数参数用于初始化相同名称的属性.

DocTest定义以下属性。它们由构造函数初始化,不应直接修改.

examples

的列表 Example编码应该由此测试运行的各个交互式Pythonexamples的对象.

globs

应该运行示例的名称空间(也称为全局变量)。这是adictionary将名称映射到值。对示例所做的命名空间的任何更改(例如绑定新变量)都将反映在globs测试运行后

name

一个字符串名称,标识DocTest。通常,这是从中提取测试的对象或文件的名称.

filename

这个文件的名称DocTest摘录自;要么None如果文件名未知,或者DocTest是从文件中提取的.

lineno

中的行号filename这个DocTest开始,或None如果行号不可用。这个行号相对于文件的开头从零开始.

docstring

从中提取测试的字符串,或None如果字符串不可用,或者测试没有从字符串中提取出来

示例对象

class doctest.Examplesource, want, exc_msg=None, lineno=0, indent=0, options=None

一个交互式示例,由Python语句及其expectedoutput组成。构造函数参数用于初始化相同名称的属性.

Example定义以下属性。它们由构造函数初始化,不应直接修改.

source

包含示例源代码的字符串。此源代码由单个Python语句组成,并始终以换行符结束;构造函数在必要时添加换行符.

want

运行示例源代码的预期输出(来自stdout,或者例外情况下的回溯)。want以newnew结尾,除非没有输出,在这种情况下它是一个空字符串。构造函数在必要时添加换行符.

exc_msg

示例生成的异常消息,如果预期示例生成异常;或None如果预计不会产生异常。将此异常消息与traceback.format_exception_only(). exc_msg的返回值进行比较,结尾为newline,而不是None。如果需要,构造函数会添加换行符.

lineno

包含此示例的字符串中的行号,其中examplebegins。这个行号相对于包含字符串的开头从零开始.

indent

示例在包含字符串中的缩进,即在示例的第一个提示符之前的空格字符数.

options

A从选项标志到TrueFalse的字典映射,用于覆盖此示例的默认选项。此字典中未包含的任何选项标志都保留其默认值(由DocTestRunneroptionflags指定)。默认情况下,没有设置任何选项.

DocTestFinder对象

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

用于提取DocTest s的处理类与给定对象相关的,与其文档字符串及其包含对象的文档字符串相关.DocTests可以从模块,类,函数,方法,静态方法,类方法和属性中提取.

可选参数verbose可用于显示查找器搜索的对象。默认为False(无输出).

可选参数parser指定用于提取的DocTestParser对象(或adrop-in替换)来自docstrings的doctests。

如果可选参数recurse是假的,那么DocTestFinder.find()只会检查给定的对象,而不是任何包含的对象.

如果可选参数exclude_empty为false,则DocTestFinder.find()将包含对具有空docstrings的对象的测试.

DocTestFinder定义以下方法:

findobj[, name][, module][, globs][, extraglobs]

返回由DocTest‘sdocstring或其任何包含对象’docstrings.obj定义的

s的列表可选参数name指定对象的名称;此名称将用于构造返回的DocTest s的名称。如果没有指定name,则使用obj.__name__.

可选参数module是包含给定对象的模块。如果未指定模块或是None,然后测试发现者将尝试自动确定正确的模块。使用对象的模块:

  • 作为默认命名空间,如果没有指定globs.
  • 阻止DocTestFinder从其他模块导入的对象中提取DocTests。(忽略包含module以外模块的包含对象。)
  • 查找包含该对象的文件的名称.
  • 帮助查找对象内的对象的行号它的文件

如果moduleFalse,不会试图找到该模块。这个isobscure,主要用于测试doctest本身:如果moduleFalse,oris None但是无法自动找到,那么所有对象都被认为属于(非存在的模块,所以所有包含的对象将(递归地)搜索doctests.

每个DocTest的全局变量是由globsextraglobs组合而成的(extraglobs中的绑定覆盖globs中的绑定)。为每个DocTest创建全局字典的newshallow副本。如果globs未指定,那么它默认为模块的__dict__,ifspecified或{}除此以外。如果没有指定extraglobs,则它默认为{}.

DocTestParser对象

class doctest.DocTestParser

用于从字符串中提取交互式示例的处理类,并使用它们创建DocTestobject.

DocTestParser定义了以下方法:

get_docteststring, globs, name, filename, lineno

从给定的字符串中提取所有doctest示例,并将它们收集到DocTest object.

globs, name, filenamelineno是新的DocTest宾语。有关更多信息,请参阅DocTest的文档.

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

从给定字符串中提取所有doctest示例,并将其作为列表返回Example对象。行号从0开始。可选参数name是一个标识该字符串的名称,仅用于错误消息.

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

将给定的字符串分为示例和插入文本,并将它们作为交替的Example s和字符串列表返回。Example s的行号是从0开始的。可选参数name是一个nameidentifying这个字符串,只用于错误消息.

DocTestRunner对象

class doctest.DocTestRunnerchecker=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 examples的实际输出进行比较的对象(或插件替换).

可选关键字参数verbose控制DocTestRunner‘sverbosity。如果verboseTrue,则会在运行时打印关于eachexample的信息。如果verboseFalse,那么只会打印失败。如果verbose未指定,或None,则使用详细输出命令行开关-v使用.

可选关键字参数optionflags可用于控制testrunner如何将预期输出与实际输出进行比较,以及它如何显示失败。有关更多信息,请参阅选项标志.

DocTestParser定义以下方法:

report_startout, test, example

报告测试运行器即将处理给定的示例。提供此方法是为了允许DocTestRunner的子类自定义其输出;它不应该被直接调用.

example是要处理的例子。test是测试containing example. out是传递给DocTestRunner.run().

report_successout, test, example, got)的输出函数

报告给定的示例成功运行。提供此方法是为了允许DocTestRunner的子类来定制它们的输出;它不应该被直接调用.

example是即将被处理的例子。got是示例中的实际输出。test是包含example. out的测试是输出到DocTestRunner.run().

report_failureout, test, example, got)的输出函数

报告给定的示例失败了。提供此方法是为了允许DocTestRunner的子类自定义其输出;它不应该直接调用.

example是要处理的例子。got是示例中的实际输出。test是包含example. out的测试是传递给DocTestRunner.run().

report_unexpected_exceptionout, test, example, exc_info)的输出函数

报告给出的例子提出了一个意外的例外。提供此方法允许DocTestRunner的子类自定义其输出;它不应该被直接调用.

example是要处理的例子。exc_info是关于意外异常的详细信息(由sys.exc_info()返回)。test是包含example. out的测试是输出到DocTestRunner.run().

runtest, compileflags=None, out=None, clear_globs=True

运行test(a DocTest object)中的示例,并使用writer函数显示结果out.

这些示例在命名空间test.globs中运行。如果clear_globsistrue(默认值),然后在测试运行后清除此命名空间,以帮助进行垃圾收集。如果您想在测试完成后检查命名空间,那么使用clear_globs=False.

compileflags给出了运行示例时Python编译器应该使用的标志集。如果未指定,则它将默认为适用于globs.

使用DocTestRunner‘soutput checker,结果由DocTestRunner.report_*()方法。

summarizeverbose=None

打印此DocTestRunner运行的所有测试用例的摘要,并返回命名的元组 TestResults(failed, attempted).

可选的verbose参数控制摘要的详细程度。如果未指定详细程度,那么DocTestRunner的详细程度将被使用.

输出检查器对象

class doctest.OutputChecker

用于检查doctest示例的实际输出是否与预期输出匹配的类。OutputChecker定义了两个方法:check_output(),它比较一对给定的输出,如果匹配则返回true;和output_difference(),它返回一个描述两个输出之间差异的字符串.

OutputChecker定义了以下方法:

check_outputwant, got, optionflags

返回True如果示例(got)的实际输出与预期的输出(want)匹配。如果它们相同,则始终认为这些字符串匹配;但是根据测试运行器使用的选项标记,也可以使用几种非精确匹配类型。有关选项标志的详细信息,请参阅选项标志

output_difference// (example, got, optionflags)

返回描述两者之间差异的字符串agiven示例(example)的预期输出和实际输出(got)。optionflags是用于比较wantgot.

调试

Doctest提供了几种调试doctest示例的机制:

  • 有几个函数将doctests转换为可执行的Python程序,可以在Python调试器下运行,pdb.

  • DebugRunnerclass是DocTestRunner表示第一个失败示例的异常,其中包含有关该示例的信息。这个信息可以用来做例子上的事后调试.

  • unittestDocTestSuite()生成的病例支持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 x6(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 x3(Pdb) step--Return--> <doctest a[2]>(1)?()->None-> f(3)(Pdb) cont(0, 3)>>>

doctests转换为Python代码,并可能在调试器下运行compositecode的函数:

doctest.script_from_exampless

将带有示例的文本转换为脚本

Argument s是包含doctest示例的字符串。该字符串被转换为Python脚本,其中s中的doctest示例转换为常规代码,其他所有内容都转换为Python注释。生成的脚本将作为字符串返回。例如,

import doctestprint(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会话转换为Pythonscript时也可以使用.

doctest.testsourcemodule, name

将对象的doctest转换为脚本.

Argument module是一个模块对象,或者是一个虚线名称模块,包含其doctests感兴趣的对象。参数name是具有感兴趣的doctests的对象的名称(在模块内)。结果是一个字符串,包含转换为Python脚本的对象的文档字符串,如上面script_from_examples()所述。例如,如果模块a.py包含顶级函数f(),那么

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

打印函数的脚本版本f()文档字符串,doctests转换为代码,其余的放在评论中.

doctest.debugmodule, name, pm=False

调试对象的doctests .

modulename参数与函数testsource()以上。命名对象的docstring的合成Python脚本被写入临时文件,然后该文件在Python调试器的控制下运行,pdb.

浅的副本module.__dict__用于本地和全局执行上下文.

可选参数pm控制是否使用事后调试。如果pm具有真值,脚本文件直接运行,并且仅当脚本通过引发未处理的异常终止时才调用调试器。如果有,那么通过pdb.post_mortem(),从未处理的异常传递traceback对象。如果pm未指定,或者为false,则脚本从一开始就在调试器下运行,通过适当的exec()调用pdb.run().

doctest.debug_srcsrc, pm=False, globs=None

在字符串中调试doctests

这就像上面的函数debug(),除了直接指定包含doctest示例的字符串,通过src论点

可选参数pm与函数debug()上面的含义相同.

可选参数globs给出一个字典用作local和globalexecution上下文。如果没有指定,或None,使用空字典。如果指定,使用字典的浅拷贝.

DebugRunner类,以及它可能引起的特殊例外,是测试框架作者最感兴趣的,并且只在这里勾勒出来。请看源代码,特别是DebugRunner的文档字符串(这是adoctest!)了解更多细节:

class doctest.DebugRunnerchecker=None, verbose=None, optionflags=0

的子类DocTestRunner一旦遇到失败,就会引发异常。如果发生意外异常,则UnexpectedException引发异常,包含测试示例和原始异常。如果输出不匹配,那么DocTestFailure引发异常,包含测试示例和实际输出.

有关构造函数参数和方法的信息,请参阅DocTestRunner中的高级API .

可以提出两个例外DebugRunner实例:

exception doctest.DocTestFailuretest, example, got

引起的异常DocTestRunner表示doctest示例的实际输出与其预期输出不匹配。构造函数参数用于初始化相同名称的属性.

DocTestFailure定义了以下属性:

DocTestFailure.test

示例失败时正在运行的DocTest对象.

DocTestFailure.example

Example失败了

DocTestFailure.got

例子的实际输出.

exception doctest.UnexpectedException (test, example, exc_info )

引起的异常DocTestRunner表示doctestexample引发了意外异常。构造函数参数用于初始化相同名称的属性.

UnexpectedException定义了以下属性:

UnexpectedException.test

示例失败时正在运行的DocTest对象.

UnexpectedException.example

Example失败了

UnexpectedException.exc_info

一个包含有关意外异常的信息的元组,由sys.exc_info().

Soapbox

返回。正如在介绍中所提到的,doctest已经变成了三个主要部分:

  1. 检查docstrings中的示例.
  2. 回归测试。
  3. 可执行文件/文化测试.

这些用法有不同的要求,区分它们很重要。特别是,用模糊的测试用例填充文档字符串会导致文档错误.

编写docstring时,请谨慎选择docstring示例。有一种艺术需要学习 – 起初可能并不自然。示例应该对文档具有真正的价值。一个很好的例子往往值得多言。如果小心谨慎,这些示例对您的用户来说将是非常宝贵的,并且随着年龄的增加和事情的变化,将会多次收回它们所需的时间。我仍然惊讶于我的一个doctest例子在“无害”改变后停止工作.

Doctest也是回归测试的绝佳工具,特别是如果你不吝啬解释性文字的话。通过交错散文和例子,跟踪实际测试的内容以及原因变得更加容易。当一个测试失败时,好的散文可以更容易弄清楚问题是什么,以及它应该如何修复。确实,你可以编写广泛的注释基于incode的测试,但很少有程序员这样做。许多人发现使用doctestapproaches会导致更清晰的测试。也许这仅仅是因为考虑编写散文比编写代码更容易,而在代码中编写注释要困难一些。我认为它不仅仅是这样:在编写基于doctest的测试时,自然的态度是你想要解释软件的优点,并用例子说明它们。这反过来自然导致以简单特征开始的测试文件并且在逻辑上进展为并发症和边缘病例。一个连贯的叙述是结果,而不是孤立的函数的集合,这些函数似乎随机地测试了一些功能。这是一种不同的态度,并产生不同的结果,模糊了测试和平台之间的区别.

回归测试最好局限于专用对象或文件。组织测试有几个选项:

  • 将包含测试用例的文本文件写为交互式示例,并使用testfile()DocFileSuite()测试文件。这是推荐的,虽然对于新项目来说是最容易做的,从一开始就设计为usedoctest.
  • 定义名为_regrtest_topic的函数,由单个文档字符串组成,包含命名主题的测试用例。这些函数可以包含在与模块相同的文件中,也可以分成单独的测试文件.
  • 定义一个__test__字典映射来自回归测试主题todocstrings包含测试用例.

当您将测试放在模块中时,模块本身就可以成为测试人员。当测试失败时,您可以安排测试运行器在调试问题时仅重新运行失败的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] 不支持包含预期输出和异常的示例。想要猜测一个结束而另一个开始的地方太容易出错,这也会导致一个令人困惑的测试.

评论被关闭。