# `doctest` ---测试交互式python示例¶

• 通过验证所有交互式示例是否仍按文档方式工作来检查模块的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()
```

```\$ python example.py
\$
```

```\$ 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.
\$
```

## 简单用法：检查docstrings中的示例¶

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

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

```python M.py
```

```python M.py -v
```

```python -m doctest -v example.py
```

## 简单用法：检查文本文件中的示例¶

doctest的另一个简单应用程序是在文本文件中测试交互式示例。这可以用 `testfile()` 功能：

```import doctest
doctest.testfile("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
```

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

```python -m doctest -v example.txt
```

## 它是如何工作的¶

### 检查哪些文档字符串？¶

```<name of M>.__test__.K
```

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

### 如何识别docstring示例？¶

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

从预期的输出中剥离出的前导空白字符数量与初始值相同。 `'>>> '` 启动示例的行。

### 例外情况呢？¶

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

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

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

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

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

### 选项标志¶

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

doctest.DONT_ACCEPT_TRUE_FOR_1

doctest.DONT_ACCEPT_BLANKLINE

doctest.NORMALIZE_WHITESPACE

doctest.ELLIPSIS

doctest.IGNORE_EXCEPTION_DETAIL

```>>> raise CustomError('message')
Traceback (most recent call last):
CustomError: message

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

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

doctest.SKIP

doctest.COMPARISON_FLAGS

doctest.REPORT_UDIFF

doctest.REPORT_CDIFF

doctest.REPORT_NDIFF

doctest.REPORT_ONLY_FIRST_FAILURE

doctest.FAIL_FAST

doctest命令行接受选项 `-f` 作为 `-o FAIL_FAST` .

3.4 新版功能.

doctest.REPORTING_FLAGS

doctest.register_optionflag(name)

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

`+``-` 以及指令选项名称。指令选项名称可以是上面解释的任何选项标志名称。

```>>> 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` 严重要求在预期输出中完全匹配。如果一个字符不匹配，则测试失败。这可能会让您吃惊几次，因为您确切地了解了Python的功能，并且不能保证输出。例如，在打印集合时，python不保证以任何特定的顺序打印元素，因此测试如下：

```>>> foo()
{"Hermione", "Harry"}
```

```>>> foo() == {"Hermione", "Harry"}
True
```

```>>> d = sorted(foo())
>>> d
['Harry', 'Hermione']
```

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

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

```>>> 1./7  # risky
0.14285714285714285
>>> print(1./7) # safer
0.142857142857
>>> print(round(1./7, 6)) # much safer
0.142857
```

```>>> 3./4  # utterly safe
0.75
```

## 基本API¶

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)

• 如果 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 指定操作系统特定的路径。路径可以是绝对路径或相对路径；相对路径是相对于当前工作目录解析的。

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

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

if可选参数 verbose 为真，即使没有失败也会生成输出。默认情况下，仅在示例失败时生成输出。

## 统一测试API¶

```import unittest
import doctest
import my_module_with_doctests

return tests
```

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

• 如果 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` ，然后是中的每个文件名 路径 指定操作系统特定的路径。路径可以是绝对路径或相对路径；相对路径是相对于当前工作目录解析的。

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

doctest.set_unittest_reportflags(flags)

## 高级API¶

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

### 对象测试对象¶

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

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

examples

globs

name

filename

lineno

docstring

### 实例对象¶

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

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

source

want

exc_msg

lineno

indent

options

### DoctestFinder对象¶

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

`DocTestFinder` 定义以下方法：

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

• 作为默认命名空间，如果 地球仪 未指定。

• 以防止doctestfinder从从其他模块导入的对象中提取doctest。（包含除 模块 被忽略。）

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

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

### doctestparser对象¶

class doctest.DocTestParser

`DocTestParser` 定义以下方法：

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

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

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

### DocteRunner对象¶

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

`DocTestParser` 定义以下方法：

report_start(out, test, example)

report_success(out, test, example, got)

report_failure(out, test, example, got)

report_unexpected_exception(out, test, example, exc_info)

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

summarize(verbose=None)

### OutputChecker对象¶

class doctest.OutputChecker

`OutputChecker` 定义以下方法：

check_output(want, got, optionflags)

output_difference(example, got, optionflags)

## 调试¶

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

• 几个函数将doctest转换为可执行的python程序，这些程序可以在python调试器下运行， `pdb` .

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

• 您可以将调用添加到 `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.script_from_examples(s)

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

doctest.testsource(module, name)

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

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

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

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

exception doctest.DocTestFailure(test, example, got)

`DocTestFailure` 定义以下属性：

DocTestFailure.test

DocTestFailure.example

DocTestFailure.got

exception doctest.UnexpectedException(test, example, exc_info)

UnexpectedException.test

UnexpectedException.example

UnexpectedException.exc_info

## 肥皂盒¶

1. 检查docstring中的示例。

2. 回归测试。

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

doctest也是一个很好的回归测试工具，特别是如果你不跳过解释性的文字。通过交错的散文和例子，可以更容易地跟踪实际测试的内容和原因。当一个测试失败时，好的散文可以让我们更容易地弄清楚问题是什么，以及应该如何解决。确实，您可以在基于代码的测试中编写大量的注释，但很少有程序员这样做。许多人发现，使用doctest方法会导致更清晰的测试。也许这仅仅是因为doctest使写散文比写代码容易一点，而用代码写注释则难一点。我认为它比这更深入：在编写基于doctest的测试时，自然的态度是，您希望解释软件的细微之处，并用示例说明它们。这反过来自然会导致从最简单的特性开始的测试文件，并在逻辑上发展到复杂和边缘情况。一个连贯的叙述是结果，而不是一个孤立的功能集合，测试孤立的功能位似乎是随机的。这是一种不同的态度，产生不同的结果，模糊了测试和解释之间的区别。

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

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

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

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