doctest
---测试交互式python示例¶
源代码: Lib/doctest.py
这个 doctest
模块搜索看起来像交互式Python会话的文本片段,然后执行这些会话以验证它们是否如图所示工作。使用doctest有几种常见方法:
通过验证所有交互式示例是否仍按文档方式工作来检查模块的docstring是否是最新的。
通过验证来自测试文件或测试对象的交互示例是否按预期工作来执行回归测试。
为一个包编写教程文档,用输入输出示例充分说明。这取决于是否强调示例或说明性文本,具有“识字测试”或“可执行文档”的味道。
下面是一个完整但很小的示例模块:
"""
This is the "example" module.
The example module supplies one function, factorial(). For example,
>>> factorial(5)
120
"""
def factorial(n):
"""Return the factorial of n, an exact integer >= 0.
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> factorial(30)
265252859812191058636308480000000
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n must be >= 0
Factorials of floats are OK, but the float must be an exact integer:
>>> factorial(30.1)
Traceback (most recent call last):
...
ValueError: n must be exact integer
>>> factorial(30.0)
265252859812191058636308480000000
It must also not be ridiculously large:
>>> factorial(1e100)
Traceback (most recent call last):
...
OverflowError: n too large
"""
import math
if not n >= 0:
raise ValueError("n must be >= 0")
if math.floor(n) != n:
raise ValueError("n must be exact integer")
if n+1 == n: # catch a value like 1e300
raise OverflowError("n too large")
result = 1
factor = 2
while factor <= n:
result *= factor
factor += 1
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
如果你运行 example.py
直接从命令行, doctest
发挥其魔力:
$ python example.py
$
没有输出!这很正常,意味着所有的例子都有效。通过 -v
到脚本,以及 doctest
打印一个详细的日志,记录它正在尝试的内容,并在结尾打印一个摘要:
$ python example.py -v
Trying:
factorial(5)
Expecting:
120
ok
Trying:
[factorial(n) for n in range(6)]
Expecting:
[1, 1, 2, 6, 24, 120]
ok
以此类推,最终以:
Trying:
factorial(1e100)
Expecting:
Traceback (most recent call last):
...
OverflowError: n too large
ok
2 items passed all tests:
1 tests in __main__
8 tests in __main__.factorial
9 tests in 2 items.
9 passed and 0 failed.
Test passed.
$
你只需要知道这些就可以开始有效利用 doctest
!跳进去。以下部分提供了完整的详细信息。请注意,标准的Python测试套件和库中有许多doctest示例。在标准测试文件中可以找到特别有用的示例 Lib/test/test_doctest.py
.
简单用法:检查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()
关于它。请注意,如果文件是包的一部分并从该包中导入其他子模块,则可能无法正常工作。
简单用法:检查文本文件中的示例¶
doctest的另一个简单应用程序是在文本文件中测试交互式示例。这可以用 testfile()
功能:
import doctest
doctest.testfile("example.txt")
该短脚本执行并验证文件中包含的任何交互式Python示例 example.txt
. 文件内容被视为一个巨大的docstring;文件不需要包含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 .
它是如何工作的¶
本节详细分析doctest是如何工作的:它查看哪些docstring,如何查找交互式示例,使用什么执行上下文,如何处理异常,以及如何使用选项标志来控制其行为。这是编写doctest示例需要知道的信息;有关在这些示例上实际运行doctest的信息,请参阅以下部分。
检查哪些文档字符串?¶
搜索模块docstring以及所有函数、类和方法docstring。不搜索导入模块的对象。
此外,如果 M.__test__
exists和“is true”,它必须是dict,并且每个条目将(字符串)名称映射到函数对象、类对象或字符串。从中找到函数和类对象docstrings M.__test__
被搜索,字符串被视为docstring。在输出中,一个键 K
在里面 M.__test__
显示名称:
<name of M>.__test__.K
找到的任何类都会进行类似的递归搜索,以测试包含方法和嵌套类中的docstring。
CPython implementation detail: Prior to version 3.4, extension modules written in C were not fully searched by doctest.
如何识别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
选项或 directive 有效。或者,可以重写测试以捕获输出并将其与作为测试一部分的预期值进行比较。在源代码中对标签的这种处理是通过反复试验得出的,并且已经证明是处理它们的最不容易出错的方法。通过编写自定义的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
从预期的输出中剥离出的前导空白字符数量与初始值相同。
'>>> '
启动示例的行。
执行上下文是什么?¶
默认情况下,每次 doctest
查找要测试的docstring,它使用 浅拷贝 属于 M
的全局变量,这样运行测试不会更改模块的实际全局变量,因此 M
不能留下不小心让另一个测试工作的碎屑。这意味着示例可以在 M
以及前面在运行的docstring中定义的名称。示例看不到在其他docstring中定义的名称。
您可以通过传递 globs=your_dict
到 testmod()
或 testfile()
相反。
例外情况呢?¶
没有问题,只要回溯是示例生成的唯一输出:只需粘贴在回溯中。 1 由于回溯包含可能快速更改的细节(例如,精确的文件路径和行号),因此Doctest很难灵活地接受它所接受的内容。
简单示例:
>>> [1, 2, 3].remove(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
如果 ValueError
是用 list.remove(x): x not in list
如图所示。
异常的预期输出必须以跟踪头开始,该头可以是以下两行中的任意一行,缩进量与示例的第一行相同:
Traceback (most recent call last):
Traceback (innermost last):
跟踪头后面跟着一个可选的跟踪堆栈,其内容被doctest忽略。回溯堆栈通常被省略,或者从交互会话中逐字复制。
回溯堆栈后面是最有趣的部分:包含异常类型和细节的行。这通常是回溯的最后一行,但如果异常具有多行细节,则可以跨多行扩展:
>>> raise ValueError('multi\n line\ndetail')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: multi
line
detail
最后三行(从 ValueError
)与异常的类型和详细信息进行比较,其余部分将被忽略。
最佳实践是省略回溯堆栈,除非它为示例添加了重要的文档值。所以最后一个例子可能更好:
>>> raise ValueError('multi\n line\ndetail')
Traceback (most recent call last):
...
ValueError: multi
line
detail
请注意,回溯处理非常特殊。特别是,在重写的示例中,使用 ...
独立于doctest ELLIPSIS
选择权。该示例中的省略号可以省略,也可以是三(或三百)个逗号或数字,或者是一个缩排的monty python skit脚本。
有些细节你应该读一次,但不需要记住:
doctest无法猜测预期的输出是来自异常回溯还是来自普通打印。例如,一个期望
ValueError: 42 is prime
是否通过ValueError
实际上是引发的,或者如果示例只打印回溯文本。在实践中,普通输出很少以回溯标题行开头,因此这不会造成真正的问题。回溯堆栈的每一行(如果存在)必须比示例的第一行缩进更多, or 从非字母数字字符开始。跟踪头后面的第一行缩进相同,以字母数字开头,被视为异常详细信息的开头。当然,这对真正的回溯是正确的。
当
IGNORE_EXCEPTION_DETAIL
指定了doctest选项,将忽略最左边冒号后面的所有内容以及异常名称中的任何模块信息。交互式shell省略了一些
SyntaxError
但doctest使用回溯标题行来区分异常和非异常。所以在罕见的情况下,你需要测试SyntaxError
如果省略了回溯标题,则需要手动将回溯标题行添加到测试示例中。
对于一些
SyntaxError
s,python显示语法错误的字符位置,使用^
标记::>>> 1 1 File "<stdin>", line 1 1 1 ^ SyntaxError: invalid syntax
由于显示错误位置的行位于异常类型和详细信息之前,因此doctest不会检查这些行。例如,以下测试将通过,即使它将
^
标记位置错误:>>> 1 1 File "<stdin>", line 1 1 1 ^ SyntaxError: invalid syntax
选项标志¶
许多选项标志控制doctest行为的各个方面。标志的符号名称作为模块常量提供,可以 bitwise ORed 一起传递给各个函数。这些名称也可用于 doctest directives ,并可以通过 -o
选择权。
3.4 新版功能: 这个 -o
命令行选项。
第一组选项定义测试语义,控制doctest如何确定实际输出是否与示例的预期输出匹配:
- doctest.DONT_ACCEPT_TRUE_FOR_1¶
默认情况下,如果预期的输出块仅包含
1
,实际输出块,仅包含1
或者只是True
被认为是一场比赛,同样0
对战False
.什么时候?DONT_ACCEPT_TRUE_FOR_1
未指定,不允许替换。默认行为迎合了Python将许多函数的返回类型从integer更改为boolean;在这些情况下,期望“小整数”输出的doctest仍然有效。这种选择可能会消失,但不会持续几年。
- doctest.DONT_ACCEPT_BLANKLINE¶
默认情况下,如果预期的输出块包含仅包含字符串的行
<BLANKLINE>
,则该行将与实际输出中的空行匹配。因为真正的空行界定了预期的输出,所以这是唯一一种传达预期是空行的方式。什么时候?DONT_ACCEPT_BLANKLINE
指定了,不允许进行此替换。
- doctest.NORMALIZE_WHITESPACE¶
指定后,所有空白序列(空白和换行)都被视为相等。预期输出中的任何空白序列都将与实际输出中的任何空白序列匹配。默认情况下,空白必须完全匹配。
NORMALIZE_WHITESPACE
当一行预期的输出非常长,并且您希望在源代码中跨多行封装它时,特别有用。
- doctest.ELLIPSIS¶
指定时,省略号标记 (
...
)在预期的输出中可以匹配实际输出中的任何子字符串。这包括跨越线边界的子字符串和空子字符串,所以最好保持这种简单的用法。复杂的使用会导致同样类型的“哎呀,太匹配了!”令人惊讶的是.*
在正则表达式中容易出现。
- doctest.IGNORE_EXCEPTION_DETAIL¶
如果指定了异常,则在引发预期类型的异常时,即使异常详细信息不匹配,也会传递预期异常的示例。例如,一个期望
ValueError: 42
如果引发的实际异常为ValueError: 3*14
,但将失败,例如,如果TypeError
提高了。它还将忽略python 3 doctest报告中使用的模块名。因此,无论测试是在python 2.7还是python 3.2(或更高版本)下运行,这两种变体都将使用指定的标志:
>>> raise CustomError('message') Traceback (most recent call last): CustomError: message >>> raise CustomError('message') Traceback (most recent call last): my_module.CustomError: message
注意
ELLIPSIS
也可用于忽略异常消息的详细信息,但根据模块详细信息是否作为异常名称的一部分打印,此类测试可能仍然失败。使用IGNORE_EXCEPTION_DETAIL
而且,python 2.3中的细节也是编写doctest的唯一明确方法,它不关心异常细节,但仍然在python 2.3或更早版本中传递(这些版本不支持 doctest directives 把它们当作无关紧要的评论来忽略。例如::>>> (1, 2)[3] = 'moo' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: object doesn't support item assignment
在指定了标志的python 2.3和更高版本的python下传递,即使python 2.4中的细节更改为“不”而不是“不”。
在 3.2 版更改:
IGNORE_EXCEPTION_DETAIL
现在还忽略与包含测试异常的模块相关的任何信息。
- doctest.SKIP¶
如果指定了,则根本不运行该示例。这在doctest示例同时作为文档和测试用例的情况下很有用,并且为了文档的目的应该包含一个示例,但不应该进行检查。例如,示例的输出可能是随机的;或者示例可能依赖于测试驱动程序不可用的资源。
跳过标志也可用于临时“注释掉”示例。
- doctest.COMPARISON_FLAGS¶
上面所有比较标志的位掩码或组合。
第二组选项控制如何报告测试失败:
- doctest.REPORT_UDIFF¶
指定时,使用统一的diff显示涉及多行预期和实际输出的故障。
- doctest.REPORT_CDIFF¶
如果指定,将使用上下文diff显示涉及多行预期和实际输出的失败。
- doctest.REPORT_NDIFF¶
如有规定,差额按
difflib.Differ
,使用与常用算法相同的算法ndiff.py
实用工具。这是唯一一种标记线内和线间差异的方法。例如,如果预期输出的行包含数字1
其中实际输出包含字母l
,插入一行,插入一个插入符号,标记不匹配的列位置。
- doctest.REPORT_ONLY_FIRST_FAILURE¶
指定后,在每个doctest中显示第一个失败的示例,但禁止所有剩余示例的输出。这将阻止doctest报告由于早期失败而中断的正确示例;但它也可能隐藏独立于第一次失败而失败的错误示例。什么时候?
REPORT_ONLY_FIRST_FAILURE
如果指定了,其余示例仍将运行,并且仍将计入报告的失败总数;仅抑制输出。
- doctest.FAIL_FAST¶
指定后,在第一个失败示例之后退出,不要尝试运行其余示例。因此,报告的失败次数最多为1。此标志在调试期间可能很有用,因为第一次失败后的示例甚至不会生成调试输出。
doctest命令行接受选项
-f
作为-o FAIL_FAST
.3.4 新版功能.
- doctest.REPORTING_FLAGS¶
一个位掩码或将上面的所有报告标志组合在一起。
还有一种注册新选项标志名的方法,不过除非您打算扩展 doctest
通过子类划分的内部结构:
- doctest.register_optionflag(name)¶
创建一个具有给定名称的新选项标志,并返回新标志的整数值。
register_optionflag()
可以在子类化时使用OutputChecker
或DocTestRunner
创建子类支持的新选项。register_optionflag()
应始终使用以下成语调用:MY_FLAG = register_optionflag('MY_FLAG')
指令¶
doctest指令可用于修改 option flags 例如。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]
注意,由于默认情况下禁用了所有选项,并且指令仅适用于它们出现在中的示例,因此启用选项(通过 +
在指令中)通常是唯一有意义的选择。但是,也可以将选项标志传递给运行doctest的函数,从而建立不同的默认值。在这种情况下,通过 -
在指令中是有用的。
警告¶
doctest
严重要求在预期输出中完全匹配。如果一个字符不匹配,则测试失败。这可能会让您吃惊几次,因为您确切地了解了Python的功能,并且不能保证输出。例如,在打印集合时,python不保证以任何特定的顺序打印元素,因此测试如下:
>>> foo()
{"Hermione", "Harry"}
很脆弱!一个解决方法是:
>>> foo() == {"Hermione", "Harry"}
True
相反。另一种方法是:
>>> d = sorted(foo())
>>> d
['Harry', 'Hermione']
注解
在python 3.6之前,当打印dict时,python不保证以任何特定的顺序打印键值对。
还有其他的,但你明白了。
另一个坏主意是打印嵌入对象地址的内容,例如:
>>> 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
在所有的平台上都是安全的,我经常设计一些教科书式的例子来产生这种形式的数字:
>>> 3./4 # utterly safe
0.75
简单的分数也更容易让人理解,这使得更好的文档。
基本API¶
功能 testmod()
和 testfile()
提供一个简单的doctest接口,该接口应足以满足大多数基本用途。有关这两个函数的不太正式的介绍,请参见第节 简单用法:检查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_relative 是
True
(the default), then filename specifies an OS-independent module-relative path. By default, this path is relative to the calling module's directory; but if the package argument is specified, then it is relative to that 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_relative 是
False
然后 filename 指定操作系统特定的路径。路径可以是绝对路径或相对路径;相对路径是相对于当前工作目录解析的。
可选参数 name 给出测试的名称;默认情况下,或者
None
,os.path.basename(filename)
使用。可选参数 包裹 是一个python包或python包的名称,该包的目录应用作模块相关文件名的基目录。如果没有指定包,那么调用模块的目录将用作模块相关文件名的基目录。指定是错误的 包裹 如果 module_relative 是
False
.可选参数 地球仪 给出在执行示例时用作全局变量的dict。这个dict的一个新的浅拷贝是为doctest创建的,因此它的示例从一张干净的石板开始。默认情况下,或如果
None
,使用新的空字典。可选参数 外生料 给出合并到用于执行示例的全局变量中的dict。这类作品
dict.update()
如果 地球仪 和 外生料 有一个公共键,在 外生料 默认情况下出现在组合字典中,或者如果None
,不使用额外的全局变量。这是一个高级功能,允许对doctest进行参数化。例如,可以使用类的通用名称为基类编写doctest,然后通过传递 外生料 dict将通用名称映射到要测试的子类。可选参数 verbose 如果为真,则打印大量内容;如果为假,则仅打印失败的内容;默认情况下,或者
None
如果并且只有当'-v'
是在sys.argv
.可选参数 报告 如果为真,则在结尾处打印摘要,否则在结尾处不打印任何内容。在详细模式中,概要是详细的,否则概要非常简短(实际上,如果所有测试都通过,则为空)。
可选参数 选择标志 (默认值0)采用 bitwise OR 选项标志的。见节 选项标志 .
可选参数 raise_on_error 默认为false。如果为true,则在示例中的第一个失败或意外异常时引发异常。这允许对失败进行事后调试。默认行为是继续运行示例。
可选参数 语法分析器 指定一个
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 应以关键字形式指定。
可从模块访问的函数和类的docstring中的测试示例 m (或模块
__main__
如果 m 未提供或None
),从m.__doc__
.也可以从dict中获取测试示例
m.__test__
,如果它存在而不是None
.m.__test__
将名称(字符串)映射到函数、类和字符串;搜索函数和类docstring以查找示例;直接搜索字符串,就像它们是docstring一样。仅附加到属于模块的对象的DocString m 被搜索。
返回
(failure_count, test_count)
.可选参数 name 给出模块的名称;默认情况下,或者
None
,m.__name__
使用。可选参数 exclude_empty 默认为false。如果为真,则不考虑未找到任何教义的对象。默认值是向后兼容性黑客,因此代码仍在使用
doctest.master.summarize()
与testmod()
继续获取没有测试的对象的输出。这个 exclude_empty 参数更新DocTestFinder
构造函数默认为true。可选参数 外生料 , verbose , 报告 , 选择标志 , raise_on_error 和 地球仪 与功能相同
testfile()
上面,除了 地球仪 默认为m.__dict__
.
- doctest.run_docstring_examples(f, globs, verbose=False, name='NoName', compileflags=None, optionflags=0)¶
与对象关联的测试示例 f 例如, f 可以是字符串、模块、函数或类对象。
字典参数的浅显副本 地球仪 用于执行上下文。
可选参数 name 在失败消息中使用,默认为
"NoName"
.if可选参数 verbose 为真,即使没有失败也会生成输出。默认情况下,仅在示例失败时生成输出。
可选参数 编译旗 给出了在运行示例时,Python编译器应该使用的一组标志。默认情况下,或如果
None
,将根据中找到的一组未来特征推断标志。 地球仪 .可选参数 选择标志 作为功能工作
testfile()
上面。
统一测试API¶
随着博士模块集合的增长,您将需要一种方法来系统地运行它们的所有博士。 doctest
提供两个可用于创建 unittest
来自模块和包含doctest的文本文件的测试套件。与…结合 unittest
测试发现,包括 load_tests()
测试模块中的功能:
import unittest
import doctest
import my_module_with_doctests
def load_tests(loader, tests, ignore):
tests.addTests(doctest.DocTestSuite(my_module_with_doctests))
return tests
创建有两个主要功能 unittest.TestSuite
文本文件和带有doctests的模块中的实例:
- doctest.DocFileSuite(*paths, module_relative=True, package=None, setUp=None, tearDown=None, globs=None, optionflags=0, parser=DocTestParser(), encoding=None)¶
将doctest测试从一个或多个文本文件转换为
unittest.TestSuite
.归还的人
unittest.TestSuite
由UnitTest框架运行,并在每个文件中运行交互式示例。如果任何文件中的一个示例失败,则合成单元测试失败,并且failureException
出现异常,显示包含测试的文件名和(有时是近似的)行号。将一个或多个路径(作为字符串)传递给要检查的文本文件。
选项可以作为关键字参数提供:
可选参数 module_relative 指定文件名在 路径 应解释为:
如果 module_relative 是
True
(the default), then each filename in paths specifies an OS-independent module-relative path. By default, this path is relative to the calling module's directory; but if the package argument is specified, then it is relative to that package. To ensure OS-independence, each filename should use/
characters to separate path segments, and may not be an absolute path (i.e., it may not begin with `` '/'。如果 module_relative 是
False
,然后是中的每个文件名 路径 指定操作系统特定的路径。路径可以是绝对路径或相对路径;相对路径是相对于当前工作目录解析的。
可选参数 包裹 是一个python包或python包的名称,该包的目录应用作中与模块相关的文件名的基目录。 路径 . 如果没有指定包,那么调用模块的目录将用作模块相关文件名的基目录。指定是错误的 包裹 如果 module_relative 是
False
.可选参数 设置 指定测试套件的设置函数。这是在运行每个文件中的测试之前调用的。这个 设置 函数将通过
DocTest
对象。设置函数可以作为 地球仪 测试的属性已通过。可选参数 拆卸 指定测试套件的下拉函数。这是在运行每个文件中的测试之后调用的。这个 拆卸 函数将通过
DocTest
对象。设置函数可以作为 地球仪 测试的属性已通过。可选参数 地球仪 是包含测试的初始全局变量的字典。将为每个测试创建此词典的新副本。默认情况下, 地球仪 是一本新的空字典。
可选参数 选择标志 指定测试的默认doctest选项,由单个选项标志创建或组合在一起。见节 选项标志 . 参见函数
set_unittest_reportflags()
下面是设置报告选项的更好方法。可选参数 语法分析器 指定一个
DocTestParser
(或子类)用于从文件中提取测试。它默认为普通的解析器(即,DocTestParser()
)可选参数 encoding 指定用于将文件转换为Unicode的编码。
全球
__file__
添加到提供给从文本文件加载的doctests的全局DocFileSuite()
.
- doctest.DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, setUp=None, tearDown=None, checker=None)¶
将模块的doctest测试转换为
unittest.TestSuite
.归还的人
unittest.TestSuite
由UnitTest框架运行,并运行模块中的每个doctest。如果任何一个理论失败,那么综合单元测试失败,并且failureException
出现异常,显示包含测试的文件名和(有时是近似的)行号。可选参数 模块 提供要测试的模块。它可以是模块对象,也可以是模块名(可能是虚线)。如果未指定,则使用调用此函数的模块。
可选参数 地球仪 是包含测试的初始全局变量的字典。将为每个测试创建此词典的新副本。默认情况下, 地球仪 是一本新的空字典。
可选参数 外生料 指定一组额外的全局变量,这些变量合并到 地球仪 . 默认情况下,不使用额外的全局参数。
可选参数 test_finder 是
DocTestFinder
对象(或插入替换项),用于从模块中提取doctest。可选参数 设置 , 拆卸 和 选择标志 与功能相同
DocFileSuite()
上面。此函数使用的搜索技术与
testmod()
.在 3.5 版更改:
DocTestSuite()
返回空的unittest.TestSuite
如果 模块 不包含docstrings而不是引发ValueError
.
在盖子下面, DocTestSuite()
创建一个 unittest.TestSuite
由于 doctest.DocTestCase
实例,以及 DocTestCase
是的子类 unittest.TestCase
. DocTestCase
这里没有记录(它是一个内部细节),但是研究它的代码可以回答关于 unittest
整合。
同样地, DocFileSuite()
创建一个 unittest.TestSuite
由于 doctest.DocFileCase
实例,以及 DocFileCase
是的子类 DocTestCase
.
所以两种方法都可以创建一个 unittest.TestSuite
运行的实例 DocTestCase
. 这一点很重要,原因很微妙:当你运行时 doctest
功能自己,你可以控制 doctest
通过将选项标志传递给 doctest
功能。但是,如果你写的是 unittest
框架, unittest
最终控制测试运行的时间和方式。框架作者通常希望控制 doctest
报告选项(例如,由命令行选项指定),但无法通过 unittest
到 doctest
测试运行道。
因此, doctest
也支持 doctest
特定于的报告标志 unittest
通过此功能支持:
- doctest.set_unittest_reportflags(flags)¶
设置
doctest
要使用的报告标志。论证 flags 拿着 bitwise OR 选项标志的。见节 选项标志 . 只能使用“报告标志”。
这是一个模块全局设置,并影响模块运行的所有将来的doctest
unittest
:runTest()
方法DocTestCase
查看为测试用例指定的选项标志,当DocTestCase
已构造实例。如果没有指定报告标志(这是典型的和预期的情况),doctest
的unittest
报告标志是 bitwise ORed 进入选项标志,这样增加的选项标志将传递给DocTestRunner
为运行doctest而创建的实例。如果在DocTestCase
实例被构造,doctest
的unittest
报告标志被忽略。的值
unittest
函数返回在调用函数之前生效的报告标志。
高级API¶
基本API是一个简单的封装器,旨在使doctest易于使用。它相当灵活,应该满足大多数用户的需求;但是,如果您需要对测试进行更细粒度的控制,或者希望扩展doctest的功能,那么您应该使用高级API。
高级API围绕两个容器类展开,这些容器类用于存储从doctest案例中提取的交互式示例:
为查找、分析和运行以及检查doctest示例,定义了其他处理类:
DocTestFinder
:查找给定模块中的所有文档字符串,并使用DocTestParser
创建一个DocTest
包含交互示例的每个docstring。DocTestParser
创建一个DocTest
字符串中的对象(例如对象的docstring)。DocTestRunner
:执行DocTest
并使用OutputChecker
以验证其输出。OutputChecker
:将doctest示例的实际输出与预期输出进行比较,并确定它们是否匹配。
这些处理类之间的关系汇总在下图中:
list of:
+------+ +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+ | ^ +---------+ | ^ (printed)
| | | Example | | |
v | | ... | v |
DocTestParser | Example | OutputChecker
+---------+
对象测试对象¶
实例对象¶
- class doctest.Example(source, want, exc_msg=None, lineno=0, indent=0, options=None)¶
一个由python语句及其预期输出组成的交互示例。构造函数参数用于初始化相同名称的属性。
Example
定义以下属性。它们由构造函数初始化,不应直接修改。- source¶
包含示例源代码的字符串。此源代码由单个python语句组成,并且始终以换行符结尾;构造函数在必要时添加换行符。
- exc_msg¶
如果期望示例生成异常,则由该示例生成的异常消息;或
None
如果不希望生成异常。将此异常消息与traceback.format_exception_only()
.exc_msg
以换行结尾,除非None
.如果需要,构造函数将添加新行。
- lineno¶
包含示例开始处的此示例的字符串中的行号。相对于包含字符串的开头,此行号为零。
- indent¶
示例在包含字符串中的缩进,即示例第一个提示之前的空格字符数。
- options¶
从选项标志到的字典映射
True
或False
,用于覆盖此示例的默认选项。未包含在此字典中的任何选项标志都保留在其默认值(由DocTestRunner
的optionflags
)默认情况下,不设置任何选项。
DoctestFinder对象¶
- class doctest.DocTestFinder(verbose=False, parser=DocTestParser(), recurse=True, exclude_empty=True)¶
用于提取
DocTest
从其docstring和所包含对象的docstring中与给定对象相关的。DocTest
可以从模块、类、函数、方法、静态方法、类方法和属性中提取。可选参数 verbose 可用于显示查找器搜索的对象。它默认为
False
(无输出)。可选参数 语法分析器 指定
DocTestParser
对象(或插入替换项),用于从docstring中提取doctest。如果可选参数 递归 是假的,那么
DocTestFinder.find()
只检查给定的对象,而不检查任何包含的对象。如果可选参数 exclude_empty 是假的,那么
DocTestFinder.find()
将包括对文档字符串为空的对象的测试。DocTestFinder
定义以下方法:- find(obj[, name][, module][, globs][, extraglobs])¶
返回
DocTest
由定义的 obj 的DocString,或由其包含的任何对象的DocString。可选参数 name 指定对象的名称;此名称将用于为返回的
DocTest
S.如果 name 未指定,则obj.__name__
使用。可选参数 模块 是包含给定对象的模块。如果模块未指定或
None
,然后测试查找器将尝试自动确定正确的模块。对象的模块用于:作为默认命名空间,如果 地球仪 未指定。
以防止doctestfinder从从其他模块导入的对象中提取doctest。(包含除 模块 被忽略。)
查找包含对象的文件名。
以帮助查找对象在其文件中的行号。
如果 模块 是
False
,不会尝试查找模块。这是模糊的,主要用于测试doctest本身:如果 模块 是False
,或None
但无法自动找到,则所有对象都被视为属于(不存在)模块,因此将(递归地)搜索包含的所有对象以查找doctest。每个人的全球
DocTest
通过组合形成 地球仪 和 外生料 (绑定在 外生料 重写中的绑定 地球仪 )为每个新的全局字典创建一个新的浅副本DocTest
.如果 地球仪 未指定,则默认为模块的 __dict__ ,如有规定,或{{}}
否则。如果 外生料 未指定,则默认为{{}}
.
doctestparser对象¶
- class doctest.DocTestParser¶
一个处理类,用于从字符串中提取交互式示例,并使用它们创建
DocTest
对象。DocTestParser
定义以下方法:- get_doctest(string, globs, name, filename, lineno)¶
从给定的字符串中提取所有doctest示例,并将它们收集到
DocTest
对象。地球仪 , name , filename 和 林诺 是新的属性
DocTest
对象。参见文档DocTest
更多信息。
DocteRunner对象¶
- 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()
.可选关键字参数 棋盘格 指定
OutputChecker
对象(或插入替换项),用于比较预期输出与doctest示例的实际输出。可选关键字参数 verbose 控制
DocTestRunner
的冗长。如果 verbose 是True
,然后在运行每个示例时打印有关该示例的信息。如果 verbose 是False
,则只打印失败。如果 verbose 未指定,或None
,然后在命令行开关上使用详细输出-v
使用。可选关键字参数 选择标志 可用于控制测试运行程序如何将预期输出与实际输出进行比较,以及如何显示失败。有关更多信息,请参见第节 选项标志 .
DocTestParser
定义以下方法:- report_start(out, test, example)¶
报告测试运行程序将要处理给定的示例。提供此方法的目的是允许
DocTestRunner
自定义它们的输出;不应该直接调用它。例子 将要处理的示例。 test 是测试 包含示例 . out 是传递给的输出函数
DocTestRunner.run()
.
- report_success(out, test, example, got)¶
报告给定示例已成功运行。提供此方法的目的是允许
DocTestRunner
自定义它们的输出;不应该直接调用它。例子 将要处理的示例。 got 是示例的实际输出。 test 测试是否包含 例子 . out 是传递给的输出函数
DocTestRunner.run()
.
- report_failure(out, test, example, got)¶
报告给定示例失败。提供此方法的目的是允许
DocTestRunner
自定义它们的输出;不应该直接调用它。例子 将要处理的示例。 got 是示例的实际输出。 test 测试是否包含 例子 . out 是传递给的输出函数
DocTestRunner.run()
.
- report_unexpected_exception(out, test, example, exc_info)¶
报告给定示例引发了意外异常。提供此方法的目的是允许
DocTestRunner
自定义它们的输出;不应该直接调用它。例子 将要处理的示例。 exc_info 是一个包含有关意外异常的信息的元组(由返回
sys.exc_info()
) test 测试是否包含 例子 . out 是传递给的输出函数DocTestRunner.run()
.
- run(test, compileflags=None, out=None, clear_globs=True)¶
在中运行示例 test (A)
DocTest
对象),并使用writer函数显示结果 out .示例在命名空间中运行
test.globs
. 如果 clear_globs 为true(默认值),则在测试运行后将清除此命名空间,以帮助垃圾收集。如果要在测试完成后检查命名空间,请使用 clear_globs=False .编译旗 给出了在运行示例时,Python编译器应该使用的一组标志。如果未指定,则它将默认为应用于 地球仪 .
每个示例的输出使用
DocTestRunner
的输出检查器,结果由DocTestRunner.report_*()
方法。
- summarize(verbose=None)¶
打印此doctestrunner运行的所有测试用例的摘要,并返回 named tuple
TestResults(failed, attempted)
.可选的 verbose 参数控制摘要的详细程度。如果未指定详细程度,则
DocTestRunner
使用了的详细信息。
OutputChecker对象¶
- class doctest.OutputChecker¶
用于检查doctest示例的实际输出是否与预期输出匹配的类。
OutputChecker
定义两种方法:check_output()
,它比较给定的输出对,并返回True
如果它们匹配;并且output_difference()
,返回描述两个输出之间差异的字符串。OutputChecker
定义以下方法:- check_output(want, got, optionflags)¶
返回
True
iff实例的实际输出( got )与预期输出匹配( want )如果这些字符串相同,则始终认为它们是匹配的;但是,根据测试运行程序使用的选项标志,也可以使用几种不精确的匹配类型。见节 选项标志 有关选项标志的详细信息。
- output_difference(example, got, optionflags)¶
返回描述给定示例的预期输出之间差异的字符串( 例子 )以及实际产量( got ) 选择标志 用于比较的选项标志集 want 和 got .
调试¶
doctest提供了调试doctest示例的几种机制:
几个函数将doctest转换为可执行的python程序,这些程序可以在python调试器下运行,
pdb
.这个
DebugRunner
类是的子类DocTestRunner
这会引发第一个失败示例的异常,其中包含有关该示例的信息。此信息可用于对示例执行事后调试。这个
unittest
案例产生者DocTestSuite()
支持debug()
方法定义人unittest.TestCase
.您可以将调用添加到
pdb.set_trace()
在doctest示例中,当执行该行时,您将进入python调试器。然后您可以检查变量的当前值,等等。例如,假设a.py
仅包含此模块docstring::""" >>> def f(x): ... g(x*2) >>> def g(x): ... print(x+3) ... import pdb; pdb.set_trace() >>> f(3) 9 """
那么一个交互式的Python会话可能如下所示:
>>> import a, doctest >>> doctest.testmod(a) --Return-- > <doctest a[1]>(3)g()->None -> import pdb; pdb.set_trace() (Pdb) list 1 def g(x): 2 print(x+3) 3 -> import pdb; pdb.set_trace() [EOF] (Pdb) p x 6 (Pdb) step --Return-- > <doctest a[0]>(2)f()->None -> g(x*2) (Pdb) list 1 def f(x): 2 -> g(x*2) [EOF] (Pdb) p x 3 (Pdb) step --Return-- > <doctest a[2]>(1)?()->None -> f(3) (Pdb) cont (0, 3) >>>
将doctest转换为python代码,并可能在调试器下运行合成代码的函数:
- doctest.script_from_examples(s)¶
将带有示例的文本转换为脚本。
论证 s 是包含doctest示例的字符串。字符串转换为python脚本,其中doctest示例位于 s 转换为常规代码,其他一切都转换为python注释。生成的脚本作为字符串返回。例如:
import doctest print(doctest.script_from_examples(r""" Set x and y to 1 and 2. >>> x, y = 1, 2 Print their sum: >>> print(x+y) 3 """))
显示::
# Set x and y to 1 and 2. x, y = 1, 2 # # Print their sum: print(x+y) # Expected: ## 3
此函数在内部由其他函数使用(请参见下文),但当您希望将交互式Python会话转换为Python脚本时,它也很有用。
- doctest.testsource(module, name)¶
将对象的doctest转换为脚本。
论证 模块 是模块对象或模块的点式名称,包含其doctest感兴趣的对象。论证 name 是具有感兴趣的doctests的对象的名称(在模块内)。结果是一个字符串,其中包含转换为python脚本的对象的docstring,如所述
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。
这个 模块 和 name 参数与for函数相同
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()
除了包含doctest示例的字符串是通过 src 参数。可选参数 pm 与函数中的含义相同
debug()
上面。可选参数 地球仪 提供一个字典,用作本地和全局执行上下文。如果未指定,或
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.got¶
示例的实际输出。
- exception doctest.UnexpectedException(test, example, exc_info)¶
由引发的异常
DocTestRunner
以指示doctest示例引发意外异常。构造函数参数用于初始化相同名称的属性。
UnexpectedException
定义以下属性:
- UnexpectedException.exc_info¶
包含有关意外异常的信息的元组,由返回
sys.exc_info()
.
肥皂盒¶
如引言所述, doctest
已经成长为有三个主要用途:
检查docstring中的示例。
回归测试。
可执行文件/识字测试。
这些用途有不同的要求,区分它们很重要。尤其是,用模糊的测试用例填充DocString会导致糟糕的文档。
编写docstring时,请小心选择docstring示例。有一种艺术是需要学习的——一开始可能并不自然。示例应该为文档增加真正的价值。一个好的例子往往值得多说。如果小心地完成,这些例子对您的用户来说将是无价的,并且会随着时间的推移和情况的变化而回报收集它们的时间。我仍然惊讶于我的 doctest
示例在“无害”更改后停止工作。
doctest也是一个很好的回归测试工具,特别是如果你不跳过解释性的文字。通过交错的散文和例子,可以更容易地跟踪实际测试的内容和原因。当一个测试失败时,好的散文可以让我们更容易地弄清楚问题是什么,以及应该如何解决。确实,您可以在基于代码的测试中编写大量的注释,但很少有程序员这样做。许多人发现,使用doctest方法会导致更清晰的测试。也许这仅仅是因为doctest使写散文比写代码容易一点,而用代码写注释则难一点。我认为它比这更深入:在编写基于doctest的测试时,自然的态度是,您希望解释软件的细微之处,并用示例说明它们。这反过来自然会导致从最简单的特性开始的测试文件,并在逻辑上发展到复杂和边缘情况。一个连贯的叙述是结果,而不是一个孤立的功能集合,测试孤立的功能位似乎是随机的。这是一种不同的态度,产生不同的结果,模糊了测试和解释之间的区别。
回归测试最好局限于专用对象或文件。组织测试有几个选项:
编写包含测试用例的文本文件作为交互式示例,并使用
testfile()
或DocFileSuite()
. 这是推荐的,虽然这是最容易做的新项目,从一开始设计使用doctest。定义名为的函数
_regrtest_topic
它由单个文档字符串组成,包含命名主题的测试用例。这些函数可以包含在与模块相同的文件中,也可以分离成单独的测试文件。定义一个
__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
不支持同时包含预期输出和异常的示例。试图猜测一个结束,另一个开始的位置太容易出错,这也会导致一个令人困惑的测试。