Skip to main content

26.3. 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.pydoctest 的工作原理是:

$ python example.py
$

没有输出!这是正常的,这意味着所有的例子工作。将 -v 传递给脚本,doctest 会打印其尝试的详细日志,并在最后打印摘要:

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

等等,最终以:

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

这是所有你需要知道,开始有效地使用 doctest!跳入。以下部分提供完整的详细信息。注意,在标准Python测试套件和库中有许多doctest的例子。特别有用的例子可以在标准测试文件 Lib/test/test_doctest.py 中找到。

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

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

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

doctest 然后检查模块 M 中的文档字符串。

将模块作为脚本运行会导致docstrings中的示例被执行和验证:

python M.py

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

而是使用 -v 开关运行它:

python M.py -v

并将所有尝试的示例的详细报告打印到标准输出,以及末尾的分类摘要。

你可以通过将 verbose=True 传递给 testmod() 来强制详细模式,或者通过传递 verbose=False 来禁止它。在任一情况下,sys.argv 不被 testmod() 检查(因此通过 -v 或不具有效果)。

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

python -m doctest -v example.py

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

有关 testmod() 的更多信息,请参见 基本API 部分。

26.3.2. 简单用法:在文本文件中检查示例

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

import doctest
doctest.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 factorial

Now use it:

    >>> factorial(6)
    120

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

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

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

默认情况下,testfile() 在调用模块的目录中查找文件。有关可选参数的说明,请参见 基本API 部分,可用于指示它在其他位置查找文件。

testmod() 一样,testfile() 的详细程度可以使用 -v 命令行开关或可选的关键字参数 verbose 来设置。

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

python -m doctest -v example.txt

因为文件名不以 .py 结尾,doctest 推断它必须使用 testfile() 运行,而不是 testmod()

有关 testfile() 的更多信息,请参见 基本API 部分。

26.3.3. 怎么运行的

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

26.3.3.1. 哪些文档字符串被检查?

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

此外,如果 M.__test__ 存在并且为“true”,则它必须是dict,并且每个条目将(字符串)名称映射到函数对象,类对象或字符串。从 M.__test__ 中找到的函数和类对象文档字符串被搜索,并且字符串被视为是docstrings。在输出中,M.__test__ 中的密钥 K 显示名称

<name of M>.__test__.K

发现的任何类都是类似地递归搜索,以测试它们所包含的方法和嵌套类中的docstrings。

CPython implementation detail: Prior to version 3.4, extension modules written in C were not fully searched by doctest.

26.3.3.2. 如何识别Docstring示例?

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

>>> # 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列制表符。由测试代码生成的输出中的标签不会被修改。因为样本输出 are 中的任何硬选项卡都已扩展,这意味着如果代码输出包含硬选项卡,则doctest可以传递的唯一方法是如果 NORMALIZE_WHITESPACE 选项或 指示 有效。或者,可以重写测试以捕获输出并将其与期望值比较作为测试的一部分。这种对源代码中的选项卡的处理是通过反复试验得到的,并且已被证明是处理它们的最小错误的方式。可以使用不同的算法通过编写自定义 DocTestParser 类来处理制表符。

  • 输出到stdout被捕获,但不输出到stderr(异常跟踪通过不同的方法捕获)。

  • 如果您在交互式会话中通过反斜杠继续一行,或者出于任何其他原因使用反斜杠,那么您应该使用原始的docstring,这将保留您输入时的反斜杠:

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

    并且从开始示例的初始 '>>> ' 行中出现的预期输出中剥离了许多前导空白字符。

26.3.3.3. 什么是执行上下文?

默认情况下,每次 doctest 找到要测试的文档字符串时,它使用 M 的全局变量的 浅拷贝,以便运行测试不会更改模块的实际全局变量,因此 M 中的一个测试不能留下面包屑,意外允许另一个测试工作。这意味着示例可以自由地使用在 M 顶层定义的任何名称,以及在运行的docstring中定义的名称。示例不能看到在其他docstrings中定义的名称。

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

26.3.3.4. 什么关于异常?

没有问题,只要traceback是示例生成的唯一输出:只需粘贴在traceback中。 [1] 由于回溯包含可能快速更改的详细信息(例如,确切的文件路径和行号),这是一种情况,其中doctest努力在其接受的灵活性。

简单的例子:

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

如果 ValueError 被提出,那么该规则成功,list.remove(x): x not in list 细节如图所示。

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

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

跟踪头之后是可选的跟踪堆栈,其内容被doctest忽略。回溯栈通常被省略,或从交互式会话逐字地复制。

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

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

最后三行(从 ValueError 开始)与异常类型和细节进行比较,其余的行被忽略。

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

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

注意,回溯被非常特殊地处理。特别地,在重写的示例中,... 的使用独立于doctest的 ELLIPSIS 选项。在该示例中的省略号可以被省略,或者也可以是三(或三百)个逗号或数字,或者蒙蒂Python skit的缩进抄本。

一些细节你应该读一次,但不需要记住:

  • Doctest无法猜测您的预期输出是来自异常跟踪还是来自普通打印。因此,例如,期望 ValueError: 42 is prime 将通过 ValueError 是否实际引发或者示例仅打印该追溯文本的示例。在实践中,普通输出很少从回溯头行开始,因此这不会产生真正的问题。

  • 回溯栈(如果存在)的每一行必须比示例的第一行缩进得更远,or 以非字母数字字符开头。跟踪回溯标题后面的第一行缩进相同,并以字母数字开头,被视为异常详细信息的开始。当然,这对正确的追溯是正确的。

  • 当指定 IGNORE_EXCEPTION_DETAIL doctest选项时,忽略最左冒号之后的所有内容以及异常名称中的任何模块信息。

  • 交互式shell省略一些 SyntaxError 的追溯头行。但是doctest使用traceback头行来区分异常和非异常。因此,在极少数情况下,您需要测试省略traceback标头的 SyntaxError,您需要手动将traceback标题行添加到测试示例中。

  • 对于某些 SyntaxError,Python使用 ^ 标记显示语法错误的字符位置:

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

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

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

26.3.3.5. 选项标志

一些选项标志控制doctest行为的各个方面。标志的符号名称作为模块常量提供,可以组合在一起并传递给各种函数。这些名称也可以在 doctest指令 中使用,并且可以通过 -o 选项传递到doctest命令行界面。

3.4 新版功能: -o 命令行选项。

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

doctest.DONT_ACCEPT_TRUE_FOR_1

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

doctest.DONT_ACCEPT_BLANKLINE

默认情况下,如果预期输出块包含仅包含字符串 <BLANKLINE> 的行,则该行将匹配实际输出中的空行。因为真正的空行界定了期望的输出,这是唯一的通信方式,预期空白行。指定 DONT_ACCEPT_BLANKLINE 时,不允许进行此替换。

doctest.NORMALIZE_WHITESPACE

指定时,所有空格(空格和换行符)的序列都被视为相等。预期输出内的任何空格序列将匹配实际输出内的任何空格序列。默认情况下,空格必须完全匹配。 NORMALIZE_WHITESPACE 在预期输出行非常长,并且希望将其包装在源中的多行时尤其有用。

doctest.ELLIPSIS

指定时,预期输出中的省略号标记(...)可以匹配实际输出中的任何子字符串。这包括跨越行边界的子字符串和空子字符串,因此最好保持对这个简单的使用。复杂的用途可以导致同样的“哎呀,它匹配太多了!意外的是 .* 在正则表达式中容易出现。

doctest.IGNORE_EXCEPTION_DETAIL

当指定时,如果引发期望类型的异常,即使异常详细信息不匹配,则期望异常的示例会传递。例如,如果提出的实际异常是 ValueError: 3*14,则期望 ValueError: 42 的示例将通过,但是将失败,例如,如果 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 ?
TypeError: object doesn't support item assignment

在Python 2.3和更高版本下传递带有指定标志的Python版本,即使Python 2.4中的细节更改为“不”而不是“不”。

在 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)

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

MY_FLAG = register_optionflag('MY_FLAG')

26.3.3.6. 指令

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))) 
[0,   1,  2,  3,  4,  5,  6,  7,  8,  9,
10,  11, 12, 13, 14, 15, 16, 17, 18, 19]

没有指令,它会失败,因为实际输出在单数列表元素之前没有两个空格,并且因为实际输出在一行上。这个测试也通过,也需要一个指令来这样做:

>>> 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的函数,建立不同的默认值。在这种情况下,通过 - 在指令中禁用选项可能很有用。

26.3.3.7. 警告

doctest 认为需要在预期产出中精确匹配。如果即使单个字符不匹配,测试也会失败。这可能会让你惊讶几次,因为你知道什么Python做和不保证输出。例如,当打印一个dict时,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 time
7948648
>>> 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  # 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

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

26.3.4. 基本API

函数 testmod()testfile() 提供了一个简单的接口,对于大多数基本用途来说都是足够的。有关这两个函数的不太正式的介绍,请参见 简单用法:检查Docstrings中的示例简单用法:在文本文件中检查示例 部分。

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

filename 之外的所有参数都是可选的,并应以关键字形式指定。

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

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

  • 如果 module_relativeTrue (默认值),则 filename 指定与操作系统无关的模块相对路径。默认情况下,此路径是相对于调用模块的目录;但如果指定了 package 参数,那么它是相对于该包。为了确保OS独立性,filename 应使用 / 字符来分隔路径段,并且可能不是绝对路径(即,它可能不以 / 开头)。

  • 如果 module_relativeFalse,则 filename 指定OS特定路径。路径可以是绝对路径或相对路径;相对路径相对于当前工作目录被解析。

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

可选参数 package 是一个Python包或一个Python包的名称,其目录应用作模块相对文件名的基本目录。如果未指定软件包,则调用模块的目录将用作模块相关文件名的基本目录。如果 module_relativeFalse,则指定 package 是错误的。

可选参数 globs 给出了在执行示例时用作全局变量的dict。这个dict的新的浅拷贝是为doctest创建的,所以它的例子从一个干净的slate开始。默认情况下,或者如果使用 None,则使用新的空字符串。

可选参数 extraglobs 将一个dict合并到用于执行示例的全局变量中。这个工作原理类似 dict.update():如果 globsextraglobs 有一个公共密钥,extraglobs 中的关联值出现在组合字典中。默认情况下,或者如果使用 None,则不使用额外的全局变量。这是一个高级功能,允许doctests的参数化。例如,可以为基类编写doctest,使用类的通用名称,然后通过将 extraglobs dict映射通用名称到要测试的子类,重新使用测试任何数量的子类。

可选参数如果为真,verbose 会打印大量内容,如果为false,则只打印失败;默认情况下,或者如果 None,当且仅当 '-v'sys.argv 中时为真。

可选参数 report 在结束时输出摘要为true,否则在结尾处不输出任何内容。在详细模式下,摘要是详细的,否则摘要非常简短(事实上,如果所有测试通过,则为空)。

可选参数 optionflags (默认值0)采用按位或选项标志。参见 选项标志

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

可选参数 parser 指定应用于从文件提取测试的 DocTestParser (或子类)。它默认为正常解析器(即 DocTestParser())。

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

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

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

在模块 m (或模块 __main__,如果 m 未提供或是 None)可达到的函数和类中的文档字符串中的文档字符串中的测试示例,从 m.__doc__ 开始。

还有测试示例可以从dict m.__test__,如果它存在并且不是 Nonem.__test__ 将名称(字符串)映射到函数,类和字符串;函数和类文档字符串搜索示例;直接搜索字符串,就好像它们是docstrings。

仅搜索附加到属于模块 m 的对象的docstrings。

返回 (failure_count, test_count)

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

可选参数 exclude_empty 默认为false。如果为true,那么没有找到任何doctest的对象将被排除在考虑之外。默认是向后兼容性黑客,所以仍然使用 doctest.master.summarize()testmod() 的代码继续获得没有测试的对象的输出。更新的 DocTestFinder 构造函数的 exclude_empty 参数默认为true。

可选参数 extraglobsverbosereportoptionflagsraise_on_errorglobs 与上面的功能 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() 一样。

26.3.5. Unittest API

随着你对doctest模块的收集的增长,你需要一种系统地运行所有doctest的方法。 doctest 提供了两个功能,可用于从包含doctests的模块和文本文件创建 unittest 测试套件。要与 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

从文本文件和模块使用doctests创建 unittest.TestSuite 实例有两个主要功能:

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 中的每个文件名指定与操作系统无关的模块相对路径。默认情况下,此路径是相对于调用模块的目录;但如果指定了 package 参数,那么它是相对于该包。为了确保OS独立性,每个文件名应使用 / 字符来分隔路径段,并且可能不是绝对路径(即,它可能不以 / 开头)。

  • 如果 module_relativeFalse,则 paths 中的每个文件名都指定一个特定于操作系统的路径。路径可以是绝对路径或相对路径;相对路径相对于当前工作目录被解析。

可选参数 package 是一个Python包或一个Python包名称,其目录应用作 paths 中模块相关文件名的基本目录。如果未指定软件包,则调用模块的目录将用作模块相关文件名的基本目录。如果 module_relativeFalse,则指定 package 是错误的。

可选参数 setUp 指定测试套件的设置函数。这在每个文件中运行测试之前调用。 setUp 函数将传递一个 DocTest 对象。 setUp函数可以在测试的 globs 属性通过时访问测试全局变量。

可选参数 tearDown 为测试套件指定拆卸函数。这在每个文件中运行测试后调用。 tearDown 函数将传递一个 DocTest 对象。 setUp函数可以在测试的 globs 属性通过时访问测试全局变量。

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

可选参数 optionflags 指定测试的默认doctest选项,这些选项是由各个选项标志一起创建的。参见 选项标志。有关更好地设置报告选项的方法,请参阅下面的函数 set_unittest_reportflags()

可选参数 parser 指定应用于从文件提取测试的 DocTestParser (或子类)。它默认为正常解析器(即 DocTestParser())。

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

全局 __file__ 添加到从使用 DocFileSuite() 的文本文件加载的doctests提供的全局变量。

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

将模块的doctest测试转换为 unittest.TestSuite

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

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

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

可选参数 extraglobs 指定一组额外的全局变量,将其合并到 globs 中。默认情况下,不使用额外的全局变量。

可选参数 test_finder 是用于从模块提取doctests的 DocTestFinder 对象(或插入替换)。

可选参数 setUptearDownoptionflags 与上面的函数 DocFileSuite() 相同。

此函数使用与 testmod() 相同的搜索技术。

在 3.5 版更改: 如果 module 不包含文档字符串而不是提高 ValueErrorDocTestSuite() 返回空 unittest.TestSuite

在封面下,DocTestSuite()doctest.DocTestCase 实例创建 unittest.TestSuiteDocTestCaseunittest.TestCase 的子类。 DocTestCase 在这里没有记录(这是一个内部细节),但研究它的代码可以回答关于 unittest 集成的确切细节的问题。

类似地,DocFileSuite()doctest.DocFileCase 实例中创建 unittest.TestSuiteDocFileCaseDocTestCase 的子类。

因此,创建 unittest.TestSuite 的两种方法都运行 DocTestCase 的实例。这是很重要的一个微妙的原因:当你自己运行 doctest 函数,你可以通过传递选项标志到 doctest 函数,直接控制 doctest 选项。然而,如果你正在编写一个 unittest 框架,unittest 最终控制测试何时以及如何运行。框架作者通常想要控制 doctest 报告选项(可能,例如,由命令行选项指定),但是没有办法通过 unittest 将选项传递给 doctest 测试运行者。

因此,doctest 还支持特定于 unittest 支持的 doctest 报告标志的概念,通过此功能:

doctest.set_unittest_reportflags(flags)

设置要使用的 doctest 报告标志。

参数 flags 采用按位或选项标志。参见 选项标志。只能使用“报告标志”。

这是一个模块全局设置,并且影响由模块 unittest 运行的所有未来doctest:DocTestCaserunTest() 方法查看在构建 DocTestCase 实例时为测试用例指定的选项标志。如果没有指定报告标志(这是典型的和期望的情况),则 doctestunittest 报告标志或者被插入到选项标志中,并且这样增加的选项标志被传递给被创建以运行doctest的 DocTestRunner 实例。如果在构造 DocTestCase 实例时指定了任何报告标志,则 doctestunittest 报告标志被忽略。

函数返回在调用函数之前有效的 unittest 报告标志的值。

26.3.6. 高级API

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

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

  • Example:单个Python statement,与其预期输出配对。

  • DocTestExample 的集合,通常从单个文档字符串或文本文件中提取。

定义了其他处理类来查找,解析和运行,并检查doctest示例:

这些处理类之间的关系总结在下图中:

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

26.3.6.1. DocTest对象

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

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

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

examples

编码应由此测试运行的各个交互式Python示例的 Example 对象的列表。

globs

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

name

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

filename

提取此 DocTest 的文件的名称;如果文件名未知,或者如果未从文件中提取 DocTest,则为 None

lineno

filename 中此 DocTest 开始的行号,如果行号不可用,则为 None。此行号相对于文件的开头是从零开始的。

docstring

从中提取测试的字符串,如果字符串不可用,则为 None,或者如果未从字符串中提取测试。

26.3.6.2. 示例对象

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

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

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

source

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

want

运行示例源代码(来自stdout或在异常情况下的跟踪)的预期输出。 want 以换行符结束,除非不需要输出,在这种情况下它是一个空字符串。构造函数在必要时添加一个换行符。

exc_msg

示例生成的异常消息,如果示例预期生成异常;或 None,如果不期望生成异常。将此异常消息与 traceback.format_exception_only() 的返回值进行比较。 exc_msg 以换行符结尾,除非它是 None。如果需要,构造函数将添加一个换行符。

lineno

包含此示例的字符串中的行号,其中示例开始。该行号相对于包含字符串的开头是从零开始的。

indent

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

options

从选项标志映射到 TrueFalse 的字典映射,用于覆盖此示例的默认选项。不包含在该字典中的任何选项标志保持其默认值(由 DocTestRunneroptionflags 指定)。默认情况下,不设置任何选项。

26.3.6.3. DocTestFinder对象

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

一个处理类,用于从其docstring和其包含对象的docstrings中提取与给定对象相关的 DocTestDocTest s可以从模块,类,函数,方法,静态方法,类方法和属性中提取。

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

可选参数 parser 指定用于从docstrings提取doctests的 DocTestParser 对象(或插入替换)。

如果可选参数 recurse 为false,那么 DocTestFinder.find() 将仅检查给定对象,而不检查任何包含的对象。

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

DocTestFinder 定义了以下方法:

find(obj[, name][, module][, globs][, extraglobs])

返回由 obj 的docstring或其任何包含的对象的docstrings定义的 DocTest 的列表。

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

可选参数 module 是包含给定对象的模块。如果模块未指定或是 None,则测试查找器将尝试自动确定正确的模块。使用对象的模块:

  • 作为默认命名空间,如果未指定 globs

  • 防止DocTestFinder从从其他模块导入的对象中提取DocTests。 (包含 module 以外的模块的对象将被忽略。)

  • 找到包含对象的文件的名称。

  • 帮助查找其文件中对象的行号。

如果 moduleFalse,则不会尝试查找模块。这是模糊的,主要在测试doctest本身使用:如果 moduleFalse,或是 None,但不能自动找到,则所有对象被认为属于(不存在)模块,因此所有包含的对象将(递归)搜索doctests。

每个 DocTest 的全局通过组合 globsextraglobsextraglobs 中的结合覆盖 globs 中的结合)形成。为每个 DocTest 创建全局字典的新的浅拷贝。如果未指定 globs,则其默认为模块的 __dict__ (如果指定),否则为 {}。如果未指定 extraglobs,则其默认为 {}

26.3.6.4. DocTestParser对象

class doctest.DocTestParser

用于从字符串中提取交互示例并使用它们创建 DocTest 对象的处理类。

DocTestParser 定义了以下方法:

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

从给定字符串中提取所有doctest示例,并将它们收集到 DocTest 对象中。

globsnamefilenamelineno 是新 DocTest 对象的属性。有关详细信息,请参阅 DocTest 的文档。

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

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

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

将给定的字符串划分为示例和中间文本,并将它们作为交替的 Example 和字符串的列表返回。 Example 的行号为0。可选参数 name 是标识此字符串的名称,仅用于错误消息。

26.3.6.5. 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 指定应用于将预期输出与doctest示例的实际输出进行比较的 OutputChecker 对象(或插入替换)。

可选的关键字参数 verbose 控制 DocTestRunner 的详细程度。如果 verboseTrue,则会在运行时打印有关每个示例的信息。如果 verboseFalse,则仅打印故障。如果 verbose 未指定或 None,则使用详细输出,如果使用命令行开关 -v

可选的关键字参数 optionflags 可用于控制测试运行器如何将预期输出与实际输出进行比较,以及如何显示故障。有关详细信息,请参阅 选项标志 部分。

DocTestParser 定义了以下方法:

report_start(out, test, example)

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

example 是要处理的示例。 test 是测试 包含示例out 是传递给 DocTestRunner.run() 的输出函数。

report_success(out, test, example, got)

报告给定示例成功运行。提供此方法以允许 DocTestRunner 的子类自定义其输出;它不应该直接调用。

example 是要处理的示例。 got 是示例的实际输出。 test 是含有 example 的试验。 out 是传递给 DocTestRunner.run() 的输出函数。

report_failure(out, test, example, got)

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

example 是要处理的示例。 got 是示例的实际输出。 test 是含有 example 的试验。 out 是传递给 DocTestRunner.run() 的输出函数。

report_unexpected_exception(out, test, example, exc_info)

报告给定的示例引发了意外的异常。提供此方法以允许 DocTestRunner 的子类自定义其输出;它不应该直接调用。

example 是要处理的示例。 exc_info 是包含有关意外异常(由 sys.exc_info() 返回的)的信息的元组。 test 是含有 example 的测试。 out 是传递给 DocTestRunner.run() 的输出函数。

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

testDocTest 对象)中运行示例,并使用写入程序函数 out 显示结果。

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

compileflags 给出了在运行示例时应由Python编译器使用的一组标志。如果未指定,那么它将默认为适用于 globs 的future-import标志集。

每个示例的输出使用 DocTestRunner 的输出检查程序进行检查,结果通过 DocTestRunner.report_*() 方法进行格式化。

summarize(verbose=None)

打印由此DocTestRunner运行的所有测试用例的摘要,并返回 named tuple TestResults(failed, attempted)

可选的 verbose 参数控制摘要的详细程度。如果没有指定详细程度,则使用 DocTestRunner 的详细程度。

26.3.6.6. OutputChecker对象

class doctest.OutputChecker

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

OutputChecker 定义了以下方法:

check_output(want, got, optionflags)

如果示例(got)的实际输出与预期输出(want)匹配,则返回 True。如果这些字符串相同,它们总是被认为是匹配的;但是根据测试运行程序正在使用的选项标志,几种非精确匹配类型也是可能的。有关选项标志的更多信息,请参见第 选项标志 节。

output_difference(example, got, optionflags)

返回描述给定示例(example)的预期输出和实际输出(got)之间的差异的字符串。 optionflags 是用于比较 wantgot 的选项标志集合。

26.3.7. 调试

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

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

  • DebugRunner 类是 DocTestRunner 的子类,引发第一个失败示例的异常,包含有关该示例的信息。此信息可用于对示例执行事后调试。

  • DocTestSuite() 生成的 unittest 案例支持 unittest.TestCase 定义的 debug() 方法。

  • 您可以在doctest示例中添加对 pdb.set_trace() 的调用,当执行该行时,您将进入Python调试器。然后可以检查变量的当前值,等等。例如,假设 a.py 仅包含此模块docstring:

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

    然后交互式Python会话可能如下所示:

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

将doctest转换为Python代码的函数,并且可能在调试器下运行合成代码:

doctest.script_from_examples(s)

将文本与示例转换为脚本。

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

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

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

显示:

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

这个函数在内部被其他函数使用(见下文),但是当你想将一个交互式Python会话转换为一个Python脚本时,也是有用的。

doctest.testsource(module, name)

将对象的doctest转换为脚本。

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

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

打印函数 f() 的docstring的脚本版本,将doctests转换为代码,其余的放在注释中。

doctest.debug(module, 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_src(src, pm=False, globs=None)

在字符串中调试doctests。

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

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

可选参数 globs 给出了一个字典,用作局部和全局执行上下文。如果未指定,或 None,则使用空字典。如果指定,则使用字典的浅拷贝。

DebugRunner 类及其可能引发的特殊例外是测试框架作者最感兴趣的,并且将只在这里草拟。查看源代码,特别是 DebugRunner 的docstring(这是一个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() 返回的意外异常信息的元组。

26.3.8. 肥皂盒

如介绍中所述,doctest 已经发展成具有三个主要用途:

  1. 检查docstrings中的示例。

  2. 回归测试。

  3. 可执行文档/识字测试。

这些用途具有不同的要求,并且区分它们是重要的。特别是,用不明确的测试用例填充文档字符串会导致错误的文档。

在编写docstring时,请谨慎选择docstring示例。有一件艺术,这需要学习—它可能不是自然的开始。示例应该为文档增加真正的价值。一个很好的例子常常值得许多话。如果小心的话,这些例子对于你的用户将是无价的,并且将回收在随着时间的推移和事情的变化而收集它们很多次所花费的时间。我仍然惊讶于我的一个 doctest 例子经常在“无害”的变化后停止工作。

Doctest也是回归测试的一个很好的工具,特别是如果你不解释文本。通过交叉散文和示例,跟踪实际测试的内容变得更容易,为什么。当测试失败时,好的散文可以使得更容易找出问题是什么,以及如何修复它。这是真的,你可以在基于代码的测试中编写广泛的评论,但很少有程序员。许多人发现使用doctest方法会导致更清晰的测试。也许这只是因为doctest使写作散文比编写代码更容易,而在代码中写评论有点困难。我认为它深入的不仅仅是:在写一个基于doctest的测试的自然态度是,你想解释你的软件的细节,并用例子说明。这反过来自然导致测试文件从最简单的功能开始,并在逻辑上进展到并发症和边缘情况。一个连贯的叙述是结果,而不是孤立的功能集合,测试孤立的功能看起来是随机的。这是一种不同的态度,产生不同的结果,模糊了测试和解释之间的区别。

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

  • 将包含测试用例的文本文件作为交互式示例,并使用 testfile()DocFileSuite() 测试文件。这是推荐,虽然是最容易做的新项目,设计从开始使用doctest。

  • 定义名为 _regrtest_topic 的函数,它由单个文档字符串组成,包含命名主题的测试用例。这些功能可以包含在与模块相同的文件中,或者分离到单独的测试文件中。

  • 定义从回归测试主题到包含测试用例的docstrings的 __test__ 字典映射。

当您将测试放在模块中时,模块本身可以是测试运行器。当测试失败时,您可以安排测试运行器在调试问题时仅重新运行失败的doctest。这里是这样的测试运行器的最小示例:

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

脚注

[1]

不支持包含预期输出和异常的示例。试图猜测一个结束和另一个开始是太容易出错,这也使混乱的测试。