25.2. doctest —测试交互式 Python 示例

doctest模块搜索看起来像交互式 Python 会话的文本,然后执行这些会话以验证它们是否按所示正常工作。有多种使用 doctest 的常用方法:

  • pass验证所有交互式示例是否仍按文档说明工作,以检查模块的文档字符串是否最新。

  • pass验证来自测试文件或测试对象的交互式示例是否按预期工作来执行回归测试。

  • 编写软件包的教程文档,并pass Importing 输出示例进行详细说明。根据是否强调示例或说明 Literals,这具有“识字测试”或“可执行文档”的风格。

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

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

    If the result is small enough to fit in an int, return an int.
    Else return a long.

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> [factorial(long(n)) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(30)
    265252859812191058636308480000000L
    >>> factorial(30L)
    265252859812191058636308480000000L
    >>> 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)
    265252859812191058636308480000000L

    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打印有关其try操作的详细日志,并在末尾显示摘要:

$ 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(long(n)) for n in range(6)]
Expecting:
    [1, 1, 2, 6, 24, 120]
ok

依此类推,finally以:

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中可以找到特别有用的示例。

25.2.1. 简单用法:检查文档字符串中的示例

开始使用 doctest 的最简单方法(但不一定是 continue 进行的方法)是使用以下命令结束每个模块M

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

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

将模块作为脚本运行会导致文档字符串中的示例得到执行和验证:

python M.py

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

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

python M.py -v

然后将所有try的示例的详细报告打印到标准输出,并在末尾附带各种摘要。

您可以pass将verbose=True传递给testmod()来强制采用冗长模式,也可以pass将verbose=False禁止它来使它成为详细模式。在这两种情况下,_5 不会被testmod()检查(因此,pass-v或不pass都没有影响)。

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

python -m doctest -v example.py

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

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

25.2.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()不会显示任何内容,除非示例失败。如果示例确实失败了,则使用与testmod()相同的格式将失败的示例和失败的原因打印到 stdout。

默认情况下,testfile()在调用模块的目录中查找文件。有关可选参数的描述,请参见Basic API部分,该参数可用于告诉其在其他位置查找文件。

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

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

python -m doctest -v example.txt

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

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

25.2.3. 这个怎么运作

本节详细检查 doctest 的工作方式:查看 docdoc 的字符串,查找交互式示例的方式,使用的执行上下文,如何处理异常以及如何使用选项标志来控制其行为。这是编写 doctest 示例所需的信息。有关在这些示例上实际运行 doctest 的信息,请参阅以下部分。

25.2.3.1. 检查哪些文档字符串?

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

另外,如果M.__test__存在且为“ true”,则它必须是字典,并且每个条目都将(字符串)名称 Map 到函数对象,类对象或字符串。搜索从M.__test__找到的函数和类对象文档字符串,并将字符串视为文档字符串。在输出中,出现M.__test__中的键K并显示名称

<name of M>.__test__.K

以类似的方式递归搜索所有找到的类,以测试其包含的方法和嵌套类中的文档字符串。

在版本 2.4 中进行了更改:不赞成使用“私人名称”概念,并且不再对其进行记录。

25.2.3.2. Docstring 示例如何识别?

在大多数情况下,交互式控制台会话的复制和粘贴效果很好,但是 doctest 并没有try对任何特定的 Python shell 进行精确的仿真。

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

任何预期的输出必须立即跟随包含代码的最后'>>> ''... '行,并且预期的输出(如果有)扩展到下一个'>>> '或全空白行。

精美印刷品:

  • 预期输出不能包含全空白行,因为采用了这样的行来表示预期输出的结束。如果预期的输出确实包含空白行,请在您的 doctest 示例中将<BLANKLINE>放在每个预期空白行的位置。

2.4 版的新Function:添加了<BLANKLINE>;在以前的版本中,无法使用包含空行的预期输出。

  • 使用 8 列制表位将所有硬制表符扩展为空格。由测试代码生成的输出中的制表符不会被修改。因为示例输出中的所有硬标签都被扩展,所以这意味着如果代码输出中包含硬标签,则 doctest 唯一可以pass的方法是NORMALIZE_WHITESPACE选项或directive是否有效。或者,可以重写测试以捕获输出,并将其与期望值进行比较,作为测试的一部分。源代码中对选项卡的这种处理是pass反复试验得出的,并且已被证明是最不容易出错的处理方式。pass编写自定义DocTestParser类,可以使用其他算法来处理制表符。

在版本 2.4 中进行了更改:将选项卡扩展到空格是新Function;以前的版本试图保留硬标签,结果令人困惑。

  • 捕获到 stdout 的输出,但不捕获到 stderr 的输出(pass其他方式捕获异常回溯)。

  • 如果您在交互式会话中pass反斜杠 continue 一行,或出于任何其他原因使用反斜杠,则应使用原始文档字符串,它将在您键入反斜杠时完全保留它们:

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

并且从预期的输出中剥离了许多开头的空白字符,就像在开始该示例的初始'>>> '行中显示的那样。

25.2.3.3. 什么是执行上下文?

默认情况下,每次doctest找到要测试的文档字符串时,它都会使用M全局变量的“浅表” *,以便运行测试不会更改模块的实际全局变量,并且M中的一个测试也不会留下意外允许其他测试起作用的碎屑。这意味着示例可以自由使用在M中顶级定义的任何名称,以及在运行的文档字符串中较早定义的名称。示例无法看到其他文档字符串中定义的名称。

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

25.2.3.4. 那么异常呢?

没问题,只要该示例产生的唯一输出是回溯即可:只需粘贴回溯即可。 [1]由于回溯包含可能会快速更改的详细信息(例如,确切的文件路径和行号),因此在这种情况下,doctest 很难灵活地接受。

Simple example:

>>> [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详细信息。

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

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

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

traceback 堆栈之后是最有趣的部分:包含异常类型和详细信息的行。这通常是回溯的最后一行,但是如果异常具有多行详细信息,则可以跨越多行:

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

将最后三行(以ValueError开头)与异常的类型和详细信息进行比较,其余的行将被忽略。

在版本 2.4 中进行了更改:以前的版本无法处理多行异常的详细信息。

最佳实践是Ellipsis回溯堆栈,除非它为示例增加了重要的文档价值。因此,最后一个示例可能更好:

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

请注意,对回溯的处理非常特殊。特别是,在重写的示例中,...的使用独立于 doctest 的ELLIPSIS选项。在该示例中,Ellipsis号可以Ellipsis,也可以是三个(或三百个)逗号或数字,或者是 Monty Python 标记的缩进抄本。

您应该阅读一次但不需要记住的一些细节:

  • Doctest 无法猜测您的预期输出是来自异常回溯还是来自普通打印。因此,例如,一个预期ValueError: 42 is prime的示例将会pass,无论实际是否引发ValueError还是该示例仅打印该 traceback 文本。实际上,普通输出很少以回溯头行开头,因此这不会造成实际问题。

  • traceback 堆栈的每一行(如果有的话)必须比示例的第一行缩进更多,或者以非字母数字字符开头。跟踪 Headers 之后的第一行以相同的缩进方式,并以字母数字开头,被视为异常详细信息的开始。当然,这对于 true 的回溯是正确的。

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

  • 交互式 Shell 程序Ellipsis了SyntaxError s 的回溯 Headers 行。但是 doctest 使用 tracebackHeaders 行将异常与非异常区分开。因此,在极少数情况下,您需要测试忽略回溯 Headers 的SyntaxError,您将需要手动将回溯 Headers 行添加到测试示例中。

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

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

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

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

25.2.3.5. 选项标志

许多选项标志控制 doctest 行为的各个方面。标志的符号名称作为模块常量提供,可以一起为bitwise ORed并传递给各种Function。这些名称也可以在doctest directives中使用。

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

  • doctest. DONT_ACCEPT_TRUE_FOR_1

    • 默认情况下,如果预期的输出块仅包含1,则仅包含1或仅True的实际输出块被视为匹配项,并且类似地针对0False进行匹配。指定DONT_ACCEPT_TRUE_FOR_1时,不允许替换。 Python 的默认行为是将许多函数的返回类型从整数更改为布尔值;期望“小整数”输出的 doctest 在这些情况下仍然有效。该选项可能会消失,但不会持续数年。
  • doctest. DONT_ACCEPT_BLANKLINE

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

    • 指定时,所有空白序列(空白和换行符)均被视为相等。预期输出中的任何空格序列都将匹配实际输出中的任何空格序列。默认情况下,空格必须完全匹配。 NORMALIZE_WHITESPACE在一行预期的输出很长并且您希望将其包装在源代码的多行中时特别有用。
  • doctest. ELLIPSIS

    • 指定后,预期输出中的Ellipsis号(...)可以与实际输出中的任何子字符串匹配。这包括跨越行边界的子字符串和空子字符串,因此最好保持简单的用法。复杂的使用可能导致相同的“糟糕,匹配太多!”令人惊讶的是正则表达式中.*容易出现。
  • doctest. IGNORE_EXCEPTION_DETAIL

    • 指定后,即使引发了预期类型的异常,即使异常详细信息不匹配,也会pass一个预期异常的示例。例如,如果实际引发的实际异常是ValueError: 3*14,则期望ValueError: 42pass的示例将失败,例如,如果引发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 的细节也是编写不关心异常细节但仍在 Python 2.3 或更早版本下 continue pass的 doctest 的唯一清晰方法(这些版本不支持doctest directives并忽略它们作为无关的 Comments) 。例如:

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

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

在 2.7 版中进行了更改:IGNORE_EXCEPTION_DETAIL现在也忽略了与包含被测异常的模块有关的任何信息

  • doctest. SKIP
    • 指定后,完全不要运行该示例。在 doctest 示例既用作文档又是测试用例的情况下,这可能很有用,并且应出于示例目的而包括一个示例,但不应对其进行检查。例如,示例的输出可能是随机的;否则示例可能取决于测试驱动程序无法使用的资源。

SKIP 标志还可以用于临时“Comments”示例。

2.5 版的新Function。

  • doctest. COMPARISON_FLAGS
    • 一个位掩码或将以上所有比较标志加在一起。

第二组选项控制如何报告测试失败:

  • doctest. REPORT_UDIFF

    • 指定后,将使用统一的差异显示涉及多行预期输出和实际输出的故障。
  • doctest. REPORT_CDIFF

    • 指定后,将使用上下文差异显示涉及多行预期输出和实际输出的故障。
  • doctest. REPORT_NDIFF

    • 指定后,差异将由difflib.Differ使用与流行的ndiff.pyUtil 相同的算法来计算。这是标记行内以及行间差异的唯一方法。例如,如果预期输出的行包含数字1,而实际输出包含字母l,则会插入带有插入符号的行,以标记不匹配的列位置。
  • doctest. REPORT_ONLY_FIRST_FAILURE

    • 指定后,在每个 doctest 中显示第一个失败的示例,但不显示所有其余示例的输出。这样可以防止 doctest 报告由于早期故障而break的正确示例;但是它也可能隐藏不正确的示例,这些示例独立于第一次失败而失败。当指定REPORT_ONLY_FIRST_FAILURE时,其余示例仍在运行,并且仍计入报告的故障总数;仅输出被抑制。
  • doctest. REPORTING_FLAGS

    • 一个位掩码或将所有上面的报告标志加在一起。

版本 2.4 中的新Function:添加了常量DONT_ACCEPT_BLANKLINENORMALIZE_WHITESPACEELLIPSISIGNORE_EXCEPTION_DETAILREPORT_UDIFFREPORT_CDIFFREPORT_NDIFFREPORT_ONLY_FIRST_FAILURECOMPARISON_FLAGSREPORTING_FLAGS

还有一种注册新选项标志名称的方法,尽管除非您打算pass子类扩展doctest内部结构,否则这没有用:

MY_FLAG = register_optionflag('MY_FLAG')

2.4 版的新Function。

25.2.3.6. Directives

Doctest 指令可用于修改单个示例的option flags。 Doctest 指令是在示例源代码之后的特殊 PythonComments:

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 的行为。使用+启用命名的行为,或使用-禁用它。

例如,此测试pass:

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

如果没有该指令,它将会失败,这是因为实际输出在单位数列表元素之前没有两个空格,并且因为实际输出在一行上。此测试也可以pass,并且还需要一条指令来这样做:

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

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

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

如果一个示例使用了多个指令 Comments,则将它们组合在一起:

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

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

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

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

2.4 版的新Function:添加了对 doctest 指令的支持。

25.2.3.7. Warnings

doctest认真要求在期望的输出中完全匹配。如果一个字符都不匹配,则测试将失败。这可能会使您感到有些惊讶,因为您确切地了解 Python 可以做什么并且不能保证输出。例如,在打印字典时,Python 不保证键值对将以任何特定 Sequences 打印,因此测试类似于

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

很脆弱!一种解决方法是

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

代替。另一个是要做

>>> d = foo().items()
>>> d.sort()
>>> 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

简单的分数也使人们更容易理解,从而可以更好地记录文档。

25.2.4. 基本 API

函数testmod()testfile()提供了一个简单的 doctest 接口,对于大多数基本用途而言已经足够了。有关这两个Function的非正式介绍,请参见简单用法:检查文档字符串中的示例简单用法:检查文本文件中的示例部分。

  • doctest. testfile(* filename [,module_relative] [,name] [,package] [,globs] [,详细] [,report] [,optionflags] [,extraglobs] [,raise_on_error] [,解析器] [,编码] * )
    • 除* filename *外的所有参数都是可选的,并且应以关键字形式指定。

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

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

  • 如果* module_relative True(默认值),则 filename 指定与 OS 无关的模块相对路径。默认情况下,该路径是相对于调用模块目录的路径。但是如果指定了 package 参数,则它相对于该软件包。为了确保 os 独立性, filename *应该使用/字符分隔路径段,并且不能是绝对路径(即,它不能以/开头)。

  • 如果* module_relative False,则 filename *指定 os 特定的路径。该路径可以是绝对路径,也可以是相对路径。相对路径相对于当前工作目录进行解析。

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

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

可选参数* globs *给出了在执行示例时用作全局变量的字典。将为 doctest 创建此 dict 的新浅表副本,因此其示例以整洁的形式开始。默认情况下,如果为None,则使用新的空字典。

可选参数* extraglobs 给出了一个 dict,它被合并到用于执行示例的全局变量中。这类似于dict.update():如果 globs extraglobs 具有公共密钥,则 extraglobs 中的关联值将出现在组合字典中。默认情况下,如果为None,则不使用额外的全局变量。这是一项高级Function,允许对 doctests 进行参数化。例如,可以使用 Base Class 的通用名称为 Base Class 编写 doctest,然后pass传递将通用名称 Map 到要测试的子类的 extraglobs * dict 来重用以测试任意数量的子类。

可选参数* verbose *如果为 true,则打印很多内容;如果为 false,则仅打印失败;否则为 false。默认情况下,如果None,则仅当'-v'sys.argv中时为 true。

可选参数* report *为 true 时在末尾显示摘要,否则末尾不显示任何内容。在详细模式下,摘要是详细的,否则摘要是非常简短的(实际上,如果所有测试都pass,则为空)。

可选参数* optionflags *或加在一起的选项标志。参见Option Flags部分。

可选参数* raise_on_error *默认为 false。如果为 true,则在示例中出现第一次失败时会引发异常或意外的异常。这样可以对故障进行事后调试。默认行为是 continue 运行示例。

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

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

2.4 版的新Function。

在版本 2.5 中更改:添加了参数* encoding *。

  • doctest. testmod(* [m] [,name] [,globs [
    • 所有参数都是可选的,除* m *外的所有参数均应以关键字形式指定。

可从模块* m (或未提供 m *或_的模块main)中以m.__doc__开头的函数和类中的文档字符串中的测试示例。

如果 dict m.__test__存在但不是None,还要测试示例。 m.__test__将名称(字符串)Map 到函数,类和字符串;搜索函数和类文档字符串以获取示例;直接搜索字符串,就好像它们是文档字符串一样。

仅搜索附加到属于模块* m *的对象的文档字符串。

返回(failure_count, test_count)

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

可选参数* exclude_empty 默认为 false。如果为 true,则不考虑未找到文档测试的对象。默认设置是向后兼容 hack,因此仍将doctest.master.summarize()testmod()结合使用的代码将 continue 获取没有测试的对象的输出。较新的DocTestFinder构造函数的 exclude_empty *参数默认为 true。

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

在版本 2.3 中进行了更改:添加了参数* optionflags *。

在版本 2.4 中进行了更改:添加了参数* extraglobs raise_on_error exclude_empty *。

在版本 2.5 中进行了更改:在 2.4 中弃用了可选参数* isprivate *。

  • doctest. run_docstring_examples(* f,globs [,verbose] [,name] [,compileflags] [,optionflags] *)
    • 与对象* f 相关的测试示例;例如, f *可以是字符串,模块,函数或类对象。

字典参数* globs *的浅表副本用于执行上下文。

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

如果可选参数* verbose *为 true,则即使没有失败,也会生成输出。默认情况下,仅在示例失败的情况下才生成输出。

可选参数* compileflags 给出了运行示例时 Python 编译器应使用的标志集。默认情况下,如果为None,则推导对应于 globs *中发现的一组将来Function的标志。

可选参数* optionflags *与上述函数testfile()相同。

25.2.5. 单元测试 API

随着文档测试模块的集合的增长,您将需要一种系统地运行其所有文档测试的方法。在 Python 2.4 之前,doctest几乎没有文档说明Tester类,它提供了一种基本方法来组合来自多个模块的 doctest。 Tester是微不足道的,实际上,大多数严肃的 Python 测试框架都基于unittest模块构建,该模块提供了许多灵活的方法来组合来自多个源的测试。因此,在 Python 2.4 中,不赞成使用doctestTester类,并且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实例有两个主要Function:

  • doctest. DocFileSuite(** paths,[module_relative] [,package] [,setUp] [,tearDown] [,globs] [,optionflags] [,解析器] [,编码] *)

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

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

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

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

  • 如果* module_relative True(默认值),则 paths 中的每个文件名都将指定与 OS 无关的模块相对路径。默认情况下,该路径是相对于调用模块目录的路径。但是如果指定了 package *参数,则它相对于该软件包。为了确保 os 独立性,每个文件名都应使用/个字符分隔路径段,并且不能是绝对路径(即,它不能以/开头)。

  • 如果* module_relative False,则 paths *中的每个文件名都指定一个特定于 os 的路径。该路径可以是绝对路径,也可以是相对路径。相对路径相对于当前工作目录进行解析。

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

可选参数* setUp *指定测试套件的设置Function。在每个文件中运行测试之前,将调用此方法。 * setUp 函数将传递一个DocTest对象。 setUp 函数可以作为pass的测试的 globs *属性访问测试全局变量。

可选参数* tearDown *指定测试套件的拆卸Function。在每个文件中运行测试后,将调用此方法。 * tearDown 函数将传递一个DocTest对象。 setUp 函数可以作为pass的测试的 globs *属性访问测试全局变量。

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

可选参数* optionflags *指定测试的默认 doctest 选项,这些选项是pass将各个选项标志或在一起创建的。参见Option Flags部分。有关设置报告选项的更好方法,请参见下面的Functionset_unittest_reportflags()

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

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

2.4 版的新Function。

在版本 2.5 中进行了更改:全局__file__已添加到提供给使用DocFileSuite()从文本文件加载的 doctest 的全局变量中。

在版本 2.5 中更改:添加了参数* encoding *。

Note

testmod()DocTestFinder不同,如果* module 不包含文档字符串,则此函数将引发ValueError。您可以pass传递DocTestFinder实例作为 test_finder 参数并将其 exclude_empty *关键字参数设置为False来防止此错误:

>>> finder = doctest.DocTestFinder(exclude_empty=False)
>>> suite = doctest.DocTestSuite(test_finder=finder)
  • doctest. DocTestSuite(* [module] [,globs] [,extraglobs] [,test_finder] [,setUp] [,tearDown] [,checker] *)

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

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

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

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

可选参数* test_finder *是DocTestFinder对象(或嵌入式替换),用于从模块中提取 doctest。

可选参数* setUp tearDown optionflags *与上面的函数DocFileSuite()相同。

2.3 版的新Function。

在版本 2.4 中更改:添加了参数* globs extraglobs test_finder setUp tearDown optionflags *;此Function现在使用与testmod()相同的搜索技术。

在幕后,DocTestSuite()doctest.DocTestCase个实例中创建了unittest.TestSuite,而DocTestCaseunittest.TestCase的子类。此处未记录DocTestCase(这是内部详细信息),但是研究其代码可以回答有关unittest集成的确切详细信息的问题。

同样,DocFileSuite()doctest.DocFileCase实例中创建unittest.TestSuite,而DocFileCaseDocTestCase的子类。

因此,创建unittest.TestSuite的两种方法都将运行DocTestCase的实例。这很重要,因为有一个微妙的原因:当您自己运行doctest函数时,可以pass将选项标志传递给doctest函数来直接控制doctest选项的使用。但是,如果您正在编写unittest框架,则unittestfinally将控制何时以及如何运行测试。框架作者通常希望控制doctest报告选项(例如,由命令行选项指定),但是无法将选项passunittest传递给doctest测试运行程序。

因此,doctest还pass以下Function支持特定于unittest支持的doctest报告标记的概念:

  • doctest. set_unittest_reportflags(* flags *)
    • 设置要使用的doctest报告标记。

参数* flags *或在一起的选项标志。参见Option Flags部分。只能使用“报告标志”。

这是模块全局设置,并且会影响模块unittest运行的所有将来的 doctest:DocTestCaserunTest()方法在构造DocTestCase实例时查看为测试用例指定的选项标志。如果未指定报告标志(这是典型的情况和预期的情况),则doctestunittest报告标志将bitwise ORed放入选项标志中,并将如此扩充的选项标志传递到为运行 doctest 而创建的DocTestRunner实例中。如果在构建DocTestCase实例时指定了任何报告标志,则doctestunittest报告标志将被忽略。

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

2.4 版的新Function。

25.2.6. 进阶 API

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

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

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

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

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

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

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

25.2.6.1. DocTest 对象

    • class * doctest. DocTest(* examples globs name filename lineno docstring *)
    • 应该在单个名称空间中运行的 doctest 示例的集合。构造函数参数用于初始化相同名称的属性。

2.4 版的新Function。

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

  • examples

    • Example个对象的列表,这些对象编码应由该测试运行的各个交互式 Python 示例。
  • globs

    • 示例应在其中运行的名称空间(即全局名称)。这是将名称 Map 到值的字典。测试运行后,示例对名称空间所做的任何更改(例如绑定新变量)都将反映在globs中。
  • name

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

    • 提取此DocTest的文件的名称;或None(如果文件名未知)或DocTest不是从文件中提取的。
  • lineno

    • DocTest开头的filename内的行号,如果该行号不可用,则返回None。该行号相对于文件的开头是从零开始的。
  • docstring

    • 从中提取测试的字符串,如果该字符串不可用,或者未从字符串中提取测试,则返回None

25.2.6.2. 示例对象

    • class * doctest. Example(* source,want [,exc_msg] [,lineno] [,indent] [,options] *)
    • 一个交互式示例,由 Python 语句及其预期输出组成。构造函数参数用于初始化相同名称的属性。

2.4 版的新Function。

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

  • source

    • 包含示例源代码的字符串。此源代码包含一个 Python 语句,并且始终以换行符结尾;构造函数在必要时添加换行符。
  • want

    • 运行示例源代码的预期输出(来自 stdout 或在异常情况下的回溯)。 want以换行符结尾,除非没有输出,否则为空字符串。构造函数在必要时添加换行符。
  • exc_msg

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

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

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

    • 从选项标志到TrueFalse的字典 Map,用于覆盖本示例的默认选项。该词典中未包含的所有选项标志均保留其默认值(由DocTestRunneroptionflags指定)。默认情况下,未设置任何选项。

25.2.6.3. DocTestFinder 对象

    • class * doctest. DocTestFinder(* [verbose] [,parser] [,recurse] [,exclude_empty] *)
    • 一个处理类,用于从其文档字符串及其包含的对象的文档字符串中提取与给定对象相关的DocTest。当前可以从以下对象类型中提取DocTest:模块,函数,类,方法,静态方法,类方法和属性。

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

可选参数* parser *指定DocTestParser对象(或嵌入式替换),该对象用于从文档字符串中提取文档测试。

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

如果可选参数* exclude_empty *为 false,则DocTestFinder.find()将包含对文档字符串为空的对象的测试。

2.4 版的新Function。

DocTestFinder定义以下方法:

  • find(* obj [,name] [,module] [,globs] [,extraglobs] *)
    • 返回由* obj *的文档字符串或其任何包含的对象的文档字符串定义的DocTest的列表。

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

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

  • 作为默认名称空间,如果未指定* globs *。

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

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

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

如果* module False,则不会try查找该模块。这是晦涩的,主要用于测试 doctest 本身:如果 module *是FalseNone但无法自动找到,则所有对象都被视为属于(不存在)模块,因此所有包含的对象将(递归)搜索 doctest。

每个DocTest的全局变量是pass组合* globs extraglobs ( extraglobs 中的绑定覆盖 globs 中的绑定)而形成的。为每个DocTest创建一个新的 globals 字典的浅表副本。如果未指定 globs ,则默认为模块的 __ dict __ (如果指定),否则为{}。如果未指定 extraglobs *,则默认为{}

25.2.6.4. DocTestParser 对象

  • 类别 doctest. DocTestParser
    • 一个处理类,用于从字符串中提取交互式示例,并使用它们创建DocTest对象。

2.4 版的新Function。

DocTestParser定义以下方法:

  • get_doctest((string globs name filename lineno *)
    • 从给定的字符串中提取所有 doctest 示例,并将它们收集到DocTest对象中。
  • globs name filename lineno *是新DocTest对象的属性。有关更多信息,请参见DocTest的文档。
  • get_examples(* string * [,* name *])

    • 从给定的字符串中提取所有 doctest 示例,并将其作为Example对象的列表返回。行号从 0 开始。可选参数* name *是标识此字符串的名称,仅用于错误消息。
  • parse(* string * [,* name *])

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

25.2.6.5. DocTestRunner 对象

    • class * doctest. DocTestRunner(* [checker] [,详细] [,optionflags] *)
    • 一个处理类,用于执行和验证DocTest中的交互式示例。

预期输出与实际输出之间的比较由OutputChecker完成。可以使用许多选项标记来自定义此比较。有关更多信息,请参见Option Flags部分。如果选项标志不足,则还可以pass将OutputChecker的子类传递给构造函数来自定义比较。

可以pass两种方式控制测试 Running 者的显示输出。首先,可以将输出函数传递给TestRunner.run();将使用应显示的字符串调用此函数。默认为sys.stdout.write。如果捕获输出不足,则还可以pass子类化 DocTestRunner 并覆盖方法report_start()report_success()report_unexpected_exception()report_failure()来自定义显示输出。

可选关键字参数* checker *指定OutputChecker对象(或直接替换),该对象应用于将预期输出与 doctest 示例的实际输出进行比较。

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

可选关键字参数* optionflags *可用于控制测试运行程序如何将预期输出与实际输出进行比较,以及显示故障的方式。有关更多信息,请参见Option Flags部分。

2.4 版的新Function。

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 *是一个 Tuples,包含有关意外异常的信息(由sys.exc_info()返回)。 * test 是包含 example *的测试。 * out *是传递给DocTestRunner.run()的输出函数。
  • run(* test [,compileflags] [,out] [,clear_globs **)
    • 运行* test (一个DocTest对象)中的示例,并使用编写器函数 out *显示结果。

这些示例在名称空间test.globs中运行。如果* clear_globs 为 true(默认设置),则该名称空间将在测试运行后清除,以帮助进行垃圾回收。如果您想在测试完成后检查名称空间,请使用 clear_globs = False *。

  • compileflags 提供了运行示例时 Python 编译器应使用的标志集。如果未指定,则默认为适用于 globs *的将来导入标志集。

使用DocTestRunner的输出检查器检查每个示例的输出,并passDocTestRunner.report_*()方法格式化结果。

  • summarize([详细])
    • 打印此 DocTestRunner 已运行的所有测试用例的摘要,并返回named tuple TestResults(failed, attempted)

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

在 2.6 版中进行了更改:使用命名的 Tuples。

25.2.6.6. OutputChecker 对象

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

2.4 版的新Function。

OutputChecker定义以下方法:

  • check_output((* want got optionflags *)

    • 如果示例的实际输出(* got )与预期的输出( want *)相匹配,则返回True。如果这些字符串相同,则始终被视为匹配。但是根据测试运行程序使用的选项标记,也可以使用几种非完全匹配类型。有关选项标志的更多信息,请参见Option Flags部分。
  • output_difference((* example got optionflags *)

    • 返回一个字符串,该字符串描述给定示例(* example )的预期输出与实际输出( got *)之间的差异。 * optionflags 是用于比较 want got *的一组选项标志。

25.2.7. Debugging

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

  • 多个函数将 doctests 转换为可执行的 Python 程序,可以在 Python 调试器pdb上运行它们。

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

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

  • 您可以在 doctest 示例中向pdb.set_trace()添加调用,然后在执行该行时将其放入 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) print 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) print x
3
(Pdb) step
--Return--
> <doctest a[2]>(1)?()->None
-> f(3)
(Pdb) cont
(0, 3)
>>>

在版本 2.4 中进行了更改:增加了在 doctests 内部有效使用pdb.set_trace()的Function。

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

  • doctest. script_from_examples(* s *)
    • 将带有示例的文本转换为脚本。

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

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

displays:

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

此函数供其他函数在内部使用(请参见下文),但在要将交互式 Python 会话转换为 Python 脚本时也很有用。

2.4 版的新Function。

  • 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()的 docstring 的脚本版本,并将 doctests 转换为代码,其余部分放在 Comments 中。

2.3 版的新Function。

  • doctest. debug(* module name * [,* pm *])
    • 调试对象的 doctest。
  • module name *参数与上面的函数testsource()相同。用于命名对象的文档字符串的合成 Python 脚本将写入一个临时文件,然后在 Python 调试器pdb的控制下运行该文件。

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

可选参数* pm 控制是否使用事后调试。如果 pm 为真值,则脚本文件将直接运行,并且仅当脚本pass引发未处理的异常终止时,调试器才会参与。如果是这样,那么将passpdb.post_mortem()调用事后调试,从未处理的异常中传递回溯对象。如果未指定 pm *或为 false,则pass将适当的execfile()调用传递给pdb.run()从一开始就在调试器下运行脚本。

2.3 版的新Function。

在版本 2.4 中更改:添加了* pm *参数。

  • doctest. debug_src(* src [,pm] [,globs] *)
    • 调试字符串中的 doctest。

类似于上面的函数debug(),不同之处在于,pass* src *参数直接指定了包含 doctest 示例的字符串。

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

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

2.4 版的新Function。

DebugRunner类及其可能引发的特殊异常是测试框架作者最感兴趣的,并且仅在此处进行草绘。有关更多详细信息,请参见源代码,尤其是DebugRunner的 docstring(这是一个 doctest!)。

    • class * doctest. DebugRunner(* [checker] [,详细] [,optionflags] *)
    • DocTestRunner的子类在遇到故障后立即引发异常。如果发生意外的异常,则会引发UnexpectedException异常,其中包含测试,示例和原始异常。如果输出不匹配,则会引发DocTestFailure异常,其中包含测试,示例和实际输出。

有关构造函数参数和方法的信息,请参见Advanced API部分中的DocTestRunner文档。

DebugRunner实例可能会引发两个异常:

    • exception * doctest. DocTestFailure(* test example got *)
    • DocTestRunner引发的异常表明 doctest 示例的实际输出与预期输出不匹配。构造函数参数用于初始化相同名称的属性。

DocTestFailure定义以下属性:

  • DocTestFailure. test

    • 示例失败时正在运行的DocTest对象。
  • DocTestFailure. example

  • DocTestFailure. got

    • 该示例的实际输出。
    • exception * doctest. UnexpectedException(* test example exc_info *)
    • DocTestRunner引发的异常表明 doctest 示例引发了意外的异常。构造函数参数用于初始化相同名称的属性。

UnexpectedException定义以下属性:

  • UnexpectedException. test

    • 示例失败时正在运行的DocTest对象。
  • UnexpectedException. example

  • UnexpectedException. exc_info

    • 一个包含有关意外异常的信息的 Tuples,由sys.exc_info()返回。

25.2.8. Soapbox

如简介中所述,doctest已 Developing 为具有三种主要用途:

  • 检查文档字符串中的示例。

  • Regression testing.

  • 可执行文件/识字测试。

这些用途有不同的要求,区分它们很重要。特别是,用晦涩的测试用例填充您的文档字符串会造成不良的文档编制。

编写文档字符串时,请小心选择文档字符串示例。这是一门需要学习的艺术,一开始可能并不自然。示例应为文档增添 true 的价值。一个好榜样通常值得多说。如果精心制作,这些示例对于您的用户将是无价的,并且将随着时间的流逝和事情的变化而返还多次收集它们所花费的时间。我对“无害”更改后doctest个示例停止工作的频率仍然感到惊讶。

Doctest 还是用于回归测试的出色工具,尤其是如果您不漏读解释性 Literals。pass交织散文和示例,可以更轻松地跟踪实际测试的内容以及原因。当测试失败时,好的散文可以使找出问题的根源以及应该如何解决变得容易得多。的确,您可以在基于代码的测试中编写大量 Comments,但很少有程序员这样做。许多人发现使用 doctest 方法可以带来更清晰的测试。也许这仅仅是因为 doctest 使编写散文比编写代码要容易一些,而在代码中编写 Comments 要困难一些。我认为这不仅限于此:在编写基于 doctest 的测试时,自然的态度是您想解释软件的优点,并pass示例进行说明。这自然会导致测试文件以最简单的Function开始,并从逻辑上 Developing 为复杂情况和极端情况。结果是连贯的叙述,而不是孤立的Function的集合,这些孤立的Function似乎随机地测试了孤立的Function。这是一种不同的态度,并产生不同的结果,从而模糊了测试和解释之间的区别。

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

  • 编写包含测试用例的文本文件作为交互式示例,并使用testfile()DocFileSuite()测试这些文件。尽管从一开始就使用 doctest 设计新项目,这是最容易做到的,但建议这样做。

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

  • 定义从回归测试主题到包含测试用例的文档字符串的__test__字典 Map。

将测试放在模块中后,该模块本身就可以成为测试运行程序。如果测试失败,则可以安排测试运行器在调试问题时仅重新运行失败的 doctest。这是此类测试运行程序的最小示例:

if __name__ == '__main__':
    import doctest
    flags = doctest.REPORT_NDIFF|doctest.REPORT_ONLY_FIRST_FAILURE
    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))

Footnotes

  • [1]
    • 不支持同时包含预期输出和异常的示例。试图猜测一个终止点和另一个终止点的位置太容易出错,这也使测试变得混乱。