Python 里主流的单元测试框架主要有 doctest、unittest 和 nose 三种。

在具体讨论每个框架之前,先看一个简单的需要测试的代码:

1
2
3
4
5
6
7
8
#!/bin/python
# fibonacci.py
def fibonacci(max):
''' calculate fibonacci series '''
a, b = 0, 1
while a < max:
yield a
a, b = b, a + b

doctest

doctest 模块让您可以在文档字符串(docstrings)内嵌入注释以显示各种语句的期望行为,尤其是函数和方法的结果。这样做很像是让文档字符串看起来如同一个交互式 shell 会话;完成这一任务的一个简单方法是,从一个 Python 交互式 shell 中(或者从 Idel、PythonWin、MacPython 或者其他带有交互式会话的 IDE 中)拷贝-粘贴。

示例

对于上面的代码,可以直接在代码的 docstring 中嵌入测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/python
# fibonacci.py

def fibonacci(max):
''' calculate fibonacci series
>>> fib = fibonacci(100)
>>> for i in fib:
... print(i, end=",")
0,1,1,2,3,5,8,13,21,34,55,89,
>>> fib = fibonacci(-1)
>>> next(fib)
Traceback (most recent call last):
...
StopIteration
>>> next(fib)
Traceback (most recent call last):
...
StopIteration
'''
a, b = 0, 1
while a < max:
yield a
a, b = b, a + b

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

末尾处的

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

表示对这个文件进行 doctest,一定要注意添加!

用法

执行下列命令即可:

1
$ python fibonacci.py -v

使用 -v 参数会提供一个包含所有测试用例的测试结果的详细报告。

这里介绍的是最常用的测试方式,还可以使用另外一个测试语句来测试代码:
1
$ python -m doctest -v fibonacci.py

如果使用这种方式,则可以不需要在实际代码上添加上述的那段引用 doctest 的语句。

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Trying:
fib = fibonacci(100)
Expecting nothing
ok
Trying:
for i in fib:
print(i, end=",")
Expecting:
0,1,1,2,3,5,8,13,21,34,55,89,
ok
Trying:
fib = fibonacci(-1)
Expecting nothing
ok
Trying:
next(fib)
Expecting:
Traceback (most recent call last):
...
StopIteration
ok
Trying:
next(fib)
Expecting:
Traceback (most recent call last):
...
StopIteration
ok
1 items had no tests:
__main__
1 items passed all tests:
5 tests in __main__.fibonacci
5 tests in 2 items.
5 passed and 0 failed.
Test passed.

unittest

unittest 已经被后面所要提到的 nose 所取代。这里不做详细介绍。如果想了解,可以参考文末提供的几个链接。

nose

nose 是一个比 unittest 更加先进的测试框架。其优势在于:

  1. 不用动不动就写个类,而只是写测试函数;
  2. 自动查找和搜集测试,不需要自己手动搭建测试集;
  3. 支持插件,可以搭配其他非常实用的标准化插件(coverage, output capture, drop into debugger on errors, doctests support, profiler)
  4. 为测试打标签,并且可以根据标签非常灵活的选择测试集;
  5. 并行测试;
  6. 更好的支持fixtures;
  7. 产生器测试。

缺点:不是在标准库自带的,需要自行安装。

示例

使用 nosetest 要求先建立好工程目录。这里假设你已经按照 打包 一节建立好了工程目录,并写好了 setup.py 文件。现在可以工程目录下建立 test 目录,然后在里头编写测试脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/python
# fibonacci_test.py

from nose.tools import *
from fib.fibonacci import fibonacci

def test_fib():
myfib = fibonacci(100)
series = []
test_case = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
for i in myfib:
series.append(i)
assert_equal(series, test_case)

说明:

  1. 注意将该脚本命名为带“test”字符串的文件,例如 “fibonacci_test.py”,否则 nose 将不会直接执行这段测试代码。
  2. 一定不要忘记在开头把 fibonacci 模块的完整路径 import 进来,否则会出错。
  3. 另外,出于安全的考虑,Linux下如果是可执行的py文件,nose就不会运行它。一个解决方案是去掉你的测试脚本的可执行权限:
1
$ chmod 644 file.py

如果你确定该测试脚本是安全的,可以在执行 nose 的时候加上 --exe 选项来让 nose 执行这些可执行的脚本。

1
$ nosetests --exe

使用方法

在工程目录下执行 nosetests ,它就会自动查找当前目录下包含"test"字符串的目录和文件进行测试。这样我们可以把所有测试放在一起,然后让测试自己去运行,我们最后看结果就可以了。我们可以指定具体如何输出结果,也可以指定如何搜索文件和文件夹,还可以把测试结果输入到指定的文件,在nosetests后面加上一些命令行参数就可以。更详细的用法可以参见官方文档

输出

1
2
3
4
5
6
test.test_fib.test_fib ... ok

----------------------------------------------------------------------
Ran 1 test in 0.004s

OK

配合插件

nose 配合其他插件可以实现更强大的功能,例如配合 coverage 插件可以检测 python 代码的测试覆盖率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[ehome@xman draft]$ nosetests -v --with-coverage --cover-package=highlight --with-doctest --cover-erase --exe
Doctest: fib.fibonacci.fibonacci ... ok
test.test_fib.test_fib ... ok

Name Stmts Miss Cover Missing
---------------------------------------------
fib/__init__ 0 0 100%
fib/fibonacci 8 2 75% 26-27
test/__init__ 0 0 100%
test/test_fib 9 0 100%
---------------------------------------------
TOTAL 17 2 88%
----------------------------------------------------------------------
Ran 2 tests in 0.022s

OK

总结

  1. 优先使用 doctest + nose 方案;
  2. 使用 nose 进行更为复杂的单元测试;
  3. 使用 doctest 测试文档与代码的一致性。

其他参考材料

  1. doctest — Test interactive Python examples
  2. Python 中的测试框架
  3. Python单元测试框架
  4. Dive Into Python3 - Unit Testing
  5. nose Document
  6. Python nose test framework介绍
  7. nose的插件
  8. 用 Python 编写干净、可测试、高质量的代码

Comments