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.py,则doctest可以发挥其魔力:

$ python example.py
$

没有输出!这是正常的,它代表所有的示例正常工作。-v传递给脚本,可以获得doctest的详细日志,并打印摘要:

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

以此类推。结尾是:

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

知道这些就够了,开始体验高效的doctest吧!当然可以更深入了解这个模块。以下部分提供完整的详细信息。请注意,标准Python测试套件和库中有很多文档测试的例子。特别有用的例子可以在标准测试文件Lib/test/test_doctest.py中找到。

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

使用doctest最简单的(但不是唯一的)方式就是在模块M结尾添加:

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

doctest将会检测模块M中的文档字符串。

当模块作为脚本运行时,文档字符串中的示例将被执行和验证:

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()的更多信息,请参阅Basic 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()不会显示任何内容。如果一个例子失败,那么使用与testmod()相同的格式将失败的例子和失败的原因输出到stdout。

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

Like testmod(), testfile()‘s verbosity can be set with the -v command-line switch or with the optional keyword argument verbose.

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

python -m doctest -v example.txt

由于文件名不以.py结尾,因此doctest推断它必须与testfile()一起运行,而不是testmod()

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

26.3.3. 它如何工作

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

26.3.3.1. 哪些Docstrings被检查?

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

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

<name of M>.__test__.K

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

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

Any expected output must immediately follow the final '>>> ' or '... ' line containing the code, and the expected output (if any) extends to the next '>>> ' or all-whitespace line.

细则:

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

  • 所有Tab字符将会被扩展为空格,使用8列制表符。测试代码生成的输出中的tab不会被修改。因为示例输出中的任何hard tab都是展开的,这意味着如果代码输出包含hard tab,则doctest可以传递的唯一方式就是NORMALIZE_WHITESPACE选项或指令有效。或者,可以重写测试以捕获输出并将其作为测试的一部分与预期值进行比较。源代码中对制表符的处理是通过反复试验得出的,并且已被证明是处理它们的最不容易出错的方式。可以通过编写一个自定义的DocTestParser类来使用不同的算法来处理选项卡。

  • 输出到标准输出被捕获,但不输出到标准错误(异常追溯通过不同的方式捕获)。

  • 如果在交互式会话中通过反斜线继续行,或者出于任何其他原因使用反斜杠,则应该使用原始文档字符串,该字符串将按照输入时的顺序保留反斜杠:

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

    否则,反斜杠将被解释为字符串的一部分。例如,上面的\n将被解释为换行符。或者,您可以在doctest版本中将每个反斜杠加倍(并且不使用原始字符串):

    >>> def f(x):
    ...     '''Backslashes in a raw docstring: m\\n'''
    >>> print(f.__doc__)
    Backslashes in a raw docstring: m\n
    
  • 起始栏无关紧要:

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

    并且从最初的>&gt;&gt;&gt; '行中出现的预期输出中除去了许多前导空白字符,例。

26.3.3.3. 什么是执行上下文?

By default, each time doctest finds a docstring to test, it uses a shallow copy of M‘s globals, so that running tests doesn’t change the module’s real globals, and so that one test in M can’t leave behind crumbs that accidentally allow another test to work. 这意味着示例可以自由使用M中顶层定义的任何名称,以及正在运行的文档字符串中已定义的名称。示例无法看到其他文档中定义的名称。

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

26.3.3.4. 如何处理异常?

没问题,只要回溯是该示例生成的唯一输出:只需粘贴回溯。[1] Since tracebacks contain details that are likely to change rapidly (for example, exact file paths and line numbers), this is one case where doctest works hard to be flexible in what it accepts.

简单的例子:

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

That doctest succeeds if ValueError is raised, with the list.remove(x): x not in list detail as shown.

预期的异常输出必须以追溯标题开头,该标题可以是以下两行中的任一行,缩写与示例的第一行相同:

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

traceback头后面跟着一个可选的traceback堆栈,其内容被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选项无关。这个例子中的省略号可以省略,或者可以是三个(或三百个)逗号或数字,或者Monty Python skit的缩进记录。

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

  • Doctest无法猜测您的预期输出是来自异常追溯还是来自普通打印。So, e.g., an example that expects ValueError: 42 is prime will pass whether ValueError is actually raised or if the example merely prints that traceback text. 实际上,普通输出很少以追溯标题行开始,所以这不会产生实际问题。

  • 回溯堆栈行(如果存在)必须比示例的首行缩进更多,以非字母数字字符开头。追溯标题后面的第一行缩写相同,并以字母数字开头,作为异常详细信息的开始。当然这对于真正的回溯来说是正确的。

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

  • 交互式shell省略了一些SyntaxError的traceback标题行。但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 directives中使用,并可以通过-o选项传递给doctest命令行界面。

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

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

文档测试 T0> DONT_ACCEPT_TRUE_FOR_1 T1> ¶ T2>

默认情况下,如果期望的输出块仅包含1,则包含1True的实际输出块被认为是匹配的,对于0False类似。当指定了DONT_ACCEPT_TRUE_FOR_1时,不允许替换。缺省行为迎合了Python将许多函数的返回类型从整数更改为布尔值;希望“小整数”输出的doctests在这些情况下仍然有效。这个选项可能会消失,但不会持续数年。

文档测试 T0> DONT_ACCEPT_BLANKLINE T1> ¶ T2>

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

文档测试 T0> NORMALIZE_WHITESPACE T1> ¶ T2>

指定时,所有空白(空格和换行符)都被视为相等。预期输出中的任何空白序列都将与实际输出中的任何空白序列相匹配。默认情况下,空白必须完全匹配。NORMALIZE_WHITESPACE is especially useful when a line of expected output is very long, and you want to wrap it across multiple lines in your source.

文档测试 T0> 省略号 T1> ¶ T2>

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

文档测试 T0> IGNORE_EXCEPTION_DETAIL T1> ¶ T2>

如果指定这个选项,那么当引发的异常的类型与期望的异常类型相同就可以通过的,不会检测异常的详细信息。例如,如果引发的实际异常是ValueError:,则会通过一个期望ValueError: 42 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

Note that ELLIPSIS can also be used to ignore the details of the exception message, but such a test may still fail based on whether or not the module details are printed as part of the exception name. Using IGNORE_EXCEPTION_DETAIL and the details from Python 2.3 is also the only clear way to write a doctest that doesn’t care about the exception detail yet continues to pass under Python 2.3 or earlier (those releases do not support doctest directives and ignore them as irrelevant comments). 例如:

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

虽然Python 2.4中的细节更改为“不”而不是“不”,但在Python 2.3以及更高版本的Python版本中通过了指定的标志。

Changed in version 3.2: IGNORE_EXCEPTION_DETAIL now also ignores any information relating to the module containing the exception under test.

文档测试 T0> SKIP T1> ¶ T2>

指定时,请不要运行该示例。这在doctest示例既可用作文档也可用作测试用例的情况下非常有用,应将其用于文档目的,但不应进行检查。例如,该例子的输出可能是随机的;或者该示例可能依赖于测试驱动程序无法使用的资源。

SKIP标志也可用于临时“注释”示例。

文档测试 T0> COMPARISON_FLAGS T1> ¶ T2>

将上面的所有比较标志掩盖起来。

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

文档测试 T0> REPORT_UDIFF T1> ¶ T2>

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

文档测试 T0> REPORT_CDIFF T1> ¶ T2>

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

文档测试 T0> REPORT_NDIFF T1> ¶ T2>

When specified, differences are computed by difflib.Differ, using the same algorithm as the popular ndiff.py utility. 这是标记线内和线间差异的唯一方法。For example, if a line of expected output contains digit 1 where actual output contains letter l, a line is inserted with a caret marking the mismatching column positions.

文档测试 T0> REPORT_ONLY_FIRST_FAILURE T1> ¶ T2>

指定时,显示每个doctest中的第一个失败示例,但禁止所有其他示例的输出。这将防止doctest报告因早期故障而中断的正确示例;但它也可能隐藏不正确的例子,不依靠第一次失败而失败。当指定REPORT_ONLY_FIRST_FAILURE时,其余示例仍在运行,并且仍计入报告的失败总数;只有输出被抑制。

文档测试 T0> FAIL_FAST T1> ¶ T2>

指定时,在第一个失败示例之后退出,不要尝试运行其余示例。因此,报告的故障数最多为1。该标志在调试期间可能很有用,因为第一次故障后的示例甚至不会产生调试输出。

doctest命令行接受选项-f作为-o FAIL_FAST的简写。

版本3.4中的新功能。

文档测试 T0> REPORTING_FLAGS T1> ¶ T2>

将上面的所有报告标记掩盖起来。

还有一种方法可以注册新的选项标记名称,但除非您打算通过子类扩展doctest内部消息,否则这种方法并不有用:

文档测试 T0> register_optionflag T1> ( T2> 名称 T3> ) T4> ¶ T5>

用给定名称创建一个新选项标志,并返回新标志的整数值。register_optionflag() can be used when subclassing OutputChecker or DocTestRunner to create new options that are supported by your subclasses. register_optionflag() should always be called using the following idiom:

MY_FLAG = register_optionflag('MY_FLAG')

26.3.3.6. 指令¶ T0>

Doctest指令可用于修改单个示例的option flagsDoctest指令是遵循示例源代码的特殊Python注释:

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

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

一个例子的doctest指令修改了doctest的这个例子的行为。使用+启用命名行为,或-将其禁用。

例如,这个测试通过:

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

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

>>> print(list(range(20))) # doctest: +ELLIPSIS
[0, 1, ..., 18, 19]

多条指令可用于单条物理线路,用逗号分隔:

>>> print(list(range(20))) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
[0,    1, ...,   18,    19]

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

>>> print(list(range(20))) # doctest: +ELLIPSIS
...                        # doctest: +NORMALIZE_WHITESPACE
[0,    1, ...,   18,    19]

如前例所示,您可以将...行添加到仅包含指令的示例中。当一个例子对于指令很容易适合同一行时太长了,这会很有用:

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

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

26.3.3.7. 警告

doctest is serious about requiring exact matches in expected output. 如果即使单个字符不匹配,测试也会失败。这可能会让你感到惊讶,因为你确切地知道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 time
7948648
>>> class C: pass
>>> C()   # the default repr() for instances embeds an address
<__main__.C instance at 0x00AC18F0>

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

>>> C() #doctest: +ELLIPSIS
<__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

The functions testmod() and testfile() provide a simple interface to doctest that should be sufficient for most basic uses. 有关这两个函数的不太正式的介绍,请参见Simple Usage: Checking Examples in DocstringsSimple Usage: Checking Examples in a Text File部分。

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

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

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

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

  • 如果module_relativeTrue(缺省值),那么filename指定一个与操作系统无关的模块相对路径。默认情况下,这个路径是相对于调用模块的目录;但是如果指定了package参数,那么它是相对于该包的。To ensure OS-independence, filename should use / characters to separate path segments, and may not be an absolute path (i.e., it may not begin with /).
  • 如果module_relativeFalse,则filename指定一个OS特定的路径。路径可以是绝对的或相对的;相对路径相对于当前工作目录被解析。

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

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

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

可选参数extraglobs给出了一个合并到用于执行示例的全局变量中的字典。这可以像dict.update()一样工作:if globsextraglobs具有共同的关键字,extraglobs t5 >出现在组合字典中。默认情况下,或者None,不使用额外的全局变量。这是一个允许doctests参数化的高级功能。例如,可以为基类编写一个doctest,使用该类的通用名称,然后通过传递将通用名称映射到子类的extraglobs字典来重新测试任意数量的子类测试。

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

可选参数报告在最后打印摘要时为true,否则在最后不打印任何内容。在详细模式下,摘要是详细的,否则摘要非常简短(实际上,如果所有测试都通过,则为空)。

可选参数optionflags(默认值0)采用按位或选项标志。请参阅Option Flags部分。

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

Optional argument parser specifies a DocTestParser (or subclass) that should be used to extract tests from the files. It defaults to a normal parser (i.e., DocTestParser()).

可选参数编码指定应该用于将文件转换为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或者,则可以从模块m(或模块__main__)访问的函数和类的docstrings中的测试示例< t5>),从m.__doc__开始。

还有可以从dict m.__test__到达的测试示例,如果它存在并且不是Nonem.__test__ maps names (strings) to functions, classes and strings; function and class docstrings are searched for examples; strings are searched directly, as if they were docstrings.

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

返回(failure_count, test_count)

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

可选参数exclude_empty默认为false。如果属实,则没有找到doctests的对象将被排除在考虑之外。缺省值是向后兼容性hack,因此仍然使用doctest.master.summarize()testmod()结合使用的代码可以继续为没有测试的对象获取输出。更新的DocTestFinder构造函数的exclude_empty参数默认为true。

可选参数extraglobsverbosereportoptionflagsraise_on_error globs与上面的函数testfile()相同,但globs默认为m.__dict__

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

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

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

可选参数name用于失败消息中,并且缺省为"NoName"

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

可选参数compileflags给出了运行示例时Python编译器应该使用的一组标志。默认情况下,或者None,将根据globs中找到的未来特征集推导出标志。

可选参数optionflags的作用与上面的函数testfile()相同。

26.3.5. Unittest API

随着您的文档测试模块集合的增长,您需要一种系统地运行所有文档测试的方法。doctest provides two functions that can be used to create unittest test suites from modules and text files containing doctests. 要与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指定应如何解释路径中的文件名:

  • 如果module_relativeTrue(缺省值),那么paths中的每个文件名指定一个独立于操作系统的模块相对路径。默认情况下,这个路径是相对于调用模块的目录;但是如果指定了package参数,那么它是相对于该包的。为了确保OS独立性,每个文件名应该使用/个字符来分隔路径段,并且可能不是绝对路径(即它可能不以/开头)。
  • 如果module_relativeFalse,那么路径中的每个文件名都指定了特定于操作系统的路径。路径可以是绝对的或相对的;相对路径相对于当前工作目录被解析。

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

可选参数setUp指定测试套件的设置函数。这在每个文件中运行测试之前被调用。The setUp function will be passed a DocTest object. setUp函数可以通过测试的globs属性来访问测试全局变量。

可选参数tearDown指定测试套件的拆卸函数。这是在每个文件中运行测试后调用的。The tearDown function will be passed a DocTest object. setUp函数可以通过测试的globs属性来访问测试全局变量。

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

可选参数optionflags指定由各个选项标志创建或组合在一起的测试的默认doctest选项。请参阅Option Flags部分。请参阅下面的函数set_unittest_reportflags()以更好地设置报告选项。

Optional argument parser specifies a DocTestParser (or subclass) that should be used to extract tests from the files. It defaults to a normal parser (i.e., DocTestParser()).

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

使用DocFileSuite()将全局__file__添加到提供给从文本文件加载的文档测试的全局变量中。

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

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

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

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

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

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

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

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

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

Changed in version 3.5: DocTestSuite() returns an empty unittest.TestSuite if module contains no docstrings instead of raising ValueError.

Under the covers, DocTestSuite() creates a unittest.TestSuite out of doctest.DocTestCase instances, and DocTestCase is a subclass of unittest.TestCase. DocTestCase isn’t documented here (it’s an internal detail), but studying its code can answer questions about the exact details of unittest integration.

Similarly, DocFileSuite() creates a unittest.TestSuite out of doctest.DocFileCase instances, and DocFileCase is a subclass of DocTestCase.

因此创建unittest.TestSuite的两种方式都运行DocTestCase的实例。This is important for a subtle reason: when you run doctest functions yourself, you can control the doctest options in use directly, by passing option flags to doctest functions. 但是,如果您正在编写一个unittest框架,unittest最终将控制何时以及如何运行测试。框架作者通常希望控制doctest报告选项(可能例如通过命令行选项指定),但是没有办法将选项通过unittest传递给doctest

For this reason, doctest also supports a notion of doctest reporting flags specific to unittest support, via this function:

文档测试 T0> set_unittest_reportflags T1> ( T2> 标志 T3> ) T4> ¶ T5>

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

参数标志采用按位或选项标志。请参阅Option Flags部分。只能使用“报告标志”。

这是一个模块全局设置,并影响模块unittest运行的所有未来文档测试:DocTestCaserunTest()方法查看选项在构造DocTestCase实例时为测试用例指定的标志。If no reporting flags were specified (which is the typical and expected case), doctest‘s unittest reporting flags are or’ed into the option flags, and the option flags so augmented are passed to the DocTestRunner instance created to run the doctest. 如果在构造DocTestCase实例时指定了任何报告标志,则忽略doctestunittest报告标志。

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

26.3.6. 高级API

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

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

  • Example:一个Python statement,与它的预期输出配对。
  • DocTest: A collection of Examples, typically extracted from a single docstring or text file.

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

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

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

26.3.6.1. DocTest Objects

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

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

DocTest defines the following attributes. 它们由构造函数初始化,不应该直接修改。

实施例 T0> ¶ T1>

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

水珠 T0> ¶ T1>

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

名称 T0> ¶ T1>

A string name identifying the DocTest. 通常,这是测试从中提取的对象或文件的名称。

文件名 T0> ¶ T1>

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

LINENO T0> ¶ T1>

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

文档字符串 T0> ¶ T1>

提取测试的字符串,如果字符串不可用,或者测试未从字符串中提取,则返回“无”。

26.3.6.2. 示例对象

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

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

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

源 T0> ¶ T1>

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

想 T0> ¶ T1>

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

exc_msg T0> ¶ T1>

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

LINENO T0> ¶ T1>

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

缩进 T0> ¶ T1>

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

选项 T0> ¶ T1>

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

26.3.6.3. DocTestFinder对象

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

一个处理类,用于从文档字符串及其包含对象的文档字符串中提取与给定对象相关的DocTestDocTests can be extracted from modules, classes, functions, methods, staticmethods, classmethods, and properties.

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

可选参数分析器指定用于从文档字符串中提取文档测试的DocTestParser对象(或插入替换)。

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

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

DocTestFinder defines the following method:

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

返回由obj的文档字符串或其包含的任何对象的文档字符串定义的DocTest的列表。

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

可选参数module是包含给定对象的模块。如果该模块没有被指定或是无,则测试发现者将尝试自动确定正确的模块。使用该对象的模块:

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

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

The globals for each DocTest is formed by combining globs and extraglobs (bindings in extraglobs override bindings in globs). 为每个DocTest创建一个新的globals字典的浅表副本。如果未指定globs,则默认为模块的__ dict __,否则为{}如果未指定extraglobs,则默认为{}

26.3.6.4. DocTestParser对象

class doctest。 DocTestParser

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

DocTestParser defines the following methods:

get_doctest stringglobsname文件名 t5 >,lineno

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

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

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

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

parse stringname ='&lt; string&gt;' ¶ T5>

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

26.3.6.5. DocTestRunner对象

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

一个处理类,用于在DocTest中执行和验证交互式示例。

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

测试运行者的显示输出可以通过两种方式进行控制。首先,输出函数可以传递给TestRunner.run();这个函数将会被显示的字符串调用。它默认为sys.stdout.write如果捕获输出是不够的,那么通过继承DocTestRunner并覆盖方法report_start()report_success()report_unexpected_exception()report_failure()

可选的关键字参数checker指定了OutputChecker对象(或插入替换),用于比较期望的输出和doctest示例的实际输出。

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

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

DocTestParser defines the following methods:

report_start(out, test, example)

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

example is the example about to be processed. test是包含示例的测试out是传递给DocTestRunner.run()的输出函数。

report_success outtest示例> ) T6> ¶ T7>

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

example is the example about to be processed. 得到是示例的实际输出。test是包含示例的测试。out是传递给DocTestRunner.run()的输出函数。

report_failure outtest示例> ) T6> ¶ T7>

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

example is the example about to be processed. 得到是示例的实际输出。test是包含示例的测试。out是传递给DocTestRunner.run()的输出函数。

report_unexpected_exception outtest示例exc_info t5 > ) T6> ¶ T7>

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

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

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

test(a DocTest对象)中运行示例,并使用写入函数out显示结果。

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

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

The output of each example is checked using the DocTestRunner‘s output checker, and the results are formatted by the DocTestRunner.report_*() methods.

总结 T0> ( T1> 详细=无 T2> ) T3> ¶ T4>

Print a summary of all the test cases that have been run by this DocTestRunner, and return a named tuple TestResults(failed, attempted).

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

26.3.6.6. OutputChecker对象

class doctest。 OutputChecker

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

OutputChecker定义了以下方法:

check_output wantgotoptionflags ¶ T6>

如果示例(got)的实际输出与预期输出(want)匹配,则返回True如果这些字符串相同,则始终认为它们匹配;但取决于测试运行器使用的选项标志,还可以使用几种非精确匹配类型。有关选项标志的更多信息,请参见Option Flags部分。

output_difference examplegotoptionflags ¶ T6>

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

26.3.7. 调试¶ T0>

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

  • Several functions convert doctests to executable Python programs, which can be run under the Python debugger, 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)
    >>>
    

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

文档测试 T0> script_from_examples T1> ( T2> 取值 T3> ) T4> ¶ T5>

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

参数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转换为脚本。

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

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

打印一个脚本版本的函数f()的文档字符串,并将文档转换为代码,其余部分放在注释中。

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

调试对象的doctests。

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

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

可选参数pm控制是否使用验尸调试。如果pm具有真值,则脚本文件将直接运行,并且调试程序只有在脚本通过引发未处理的异常终止时才会涉及。如果是,则通过pdb.post_mortem()调用验尸调试,从未处理的异常中传递回溯对象。如果没有指定pm,或者为false,则通过将适当的exec()调用传递给pdb.run()

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

用字符串调试doctests。

这与上面的函数debug()类似,只是通过src参数直接指定了包含doctest示例的字符串。

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

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

DebugRunner类以及它可能引发的特殊异常是测试框架作者最感兴趣的,并且在此仅作简要说明。查看源代码,特别是DebugRunner的文档字符串(这是一个doctest!)更多细节:

class doctest。 DebugRunner checker =无verbose =无optionflags = 0

DocTestRunner的子类一旦遇到故障就会引发异常。如果发生意外异常,将引发UnexpectedException异常,其中包含测试,示例和原始异常。如果输出不匹配,则会引发一个DocTestFailure异常,其中包含测试,示例和实际输出。

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

There are two exceptions that may be raised by DebugRunner instances:

exception doctest.DocTestFailure(test, example, got)

An exception raised by DocTestRunner to signal that a doctest example’s actual output did not match its expected output. 构造函数参数用于初始化相同名称的属性。

DocTestFailure defines the following attributes:

DocTestFailure。 T0> 测试 T1> ¶ T2>

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

DocTestFailure。 T0> 例如 T1> ¶ T2>

失败的Example

DocTestFailure。 T0> 得到 T1> ¶ T2>

示例的实际输出。

异常 doctest。 UnexpectedException testexampleexc_info

An exception raised by DocTestRunner to signal that a doctest example raised an unexpected exception. 构造函数参数用于初始化相同名称的属性。

UnexpectedException定义了以下属性:

UnexpectedException。 T0> 测试 T1> ¶ T2>

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

UnexpectedException。 T0> 例如 T1> ¶ T2>

失败的Example

UnexpectedException。 T0> exc_info T1> ¶ T2>

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

26.3.8. 肥皂盒¶ T0>

As mentioned in the introduction, doctest has grown to have three primary uses:

  1. 检查文档字符串中的示例。
  2. 回归测试。
  3. 可执行文档/文字测试。

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

在编写文档字符串时,请小心选择文档字符串示例。有一件艺术需要学习 - 起初可能并不自然。示例应该为文档增加真正的价值。一个很好的例子往往可以说很多话。如果谨慎处理,这些示例对您的用户来说将是非常宝贵的,并且会随着时间的推移和事情的变化而回报多次收集它们所需的时间。我仍然惊讶于我的一个doctest示例在“无害”更改后停止工作的频率。

Doctest也是回归测试的绝佳工具,特别是如果你不吝啬解释性文本。通过插入散文和例子,跟踪实际正在测试的内容以及为什么更容易。当一个测试失败时,好的散文可以使得更容易找出问题所在,以及应该如何解决问题。的确,您可以在基于代码的测试中编写大量的评论,但很少有程序员会这样做。许多人已经发现使用doctest方法会导致更清晰的测试。也许这只是因为doctest使编写散文比编写代码容易一些,而在代码中编写注释有点困难。我认为它比以上更深刻:编写基于doctest的测试时的自然态度是您想解释软件的优点,并用示例来说明它们。这反过来自然会导致以最简单的功能开始的测试文件,并在逻辑上进展到复杂性和边缘情况。一个连贯的叙述是结果,而不是一组孤立的函数,它们似乎随机地测试孤立的功能位。这是一种不同的态度,产生不同的结果,模糊了测试和解释之间的区别。

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

  • 将包含测试用例的文本文件编写为交互式示例,并使用testfile()DocFileSuite()测试文件。这是推荐的,尽管对于从一开始就使用doctest设计的新项目来说,这是最容易做到的。
  • 定义名为_regrtest_topic的函数,其中包含单个文档字符串,其中包含指定主题的测试用例。这些功能可以包含在与模块相同的文件中,或者分离到单独的测试文件中。
  • Define a __test__ dictionary mapping from regression test topics to docstrings containing test cases.

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