6.5 测试序列与并行

在执行大量测试时,按它们被运行的情况进行分析是很有用的。类似nosetests这样的工具只是将结果输出到stdout,即标准输出,但这对测试结果的解析或分析并不方便。

subunit(https://pypi.python.org/pypi/python-subunit)是用来为测试结果提供流协议(streaming protocol)的一个Python模块。它支持很多有意思的功能,如聚合测试结果1或者对测试的运行进行记录或归档等。

广告:个人专属 VPN,独立 IP,流量大,速度快,连接稳定,多机房切换,每月最低仅 10 美元

使用subunit运行测试非常简单:

$ python -m subunit.run test_scenario

这条命令的输出是二进制数据,所以除非有能力直接阅读subunit协议,否则在这里直接再现它的输出结果实在是没什么意义。不过,subunit还支持一组将其二进制流转换为其他易读格式的工具,如示例6.12所示。

示例6.12 使用subunit2pyunit

$ python -m subunit.run test_scenario | subunit2pyunit
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Not
found)
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Not
found) ... ok
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Client
error)
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Client
error) ... ok
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Server
error)
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Server
error) ... ok

---------------------------------------------------------
Ran 3 tests in 0.061s
OK

这样的结果就容易理解了。你应该可以认出这个关于场景测试的测试集来自6.4节。其他值得一提的工具还有subunit2csvsubunit2gtksubunit2junitxml

subunit还可以通过传入discover参数支持自动发现哪个测试要运行。

$ python -m subunit.run discover | subunit2pyunit
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Not
found)
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Not
found) ... ok
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Client
error)
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Client
error) ... ok
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Server
error)
test_scenario.TestPythonErrorCode.test_python_status_code_handling(Server
error) ... ok

---------------------------------------------------------
Ran 3 tests in 0.061s
OK

也可以通过传入参数--list只列出测试但不运行。要查看这一结果,可以使用subunit-ls

$ python -m subunit.run discover --list | subunit-ls --exists
test_request.TestPython.test_bad_status_code
test_request.TestPython.test_ioerror
test_request.TestPython.test_python_is
test_request.TestPython.test_python_is_not
test_scenario.TestPythonErrorCode.test_python_status_code_handling

 提示

  可以使用--load-list选项指定要运行的测试的清单而不是运行所有的测试。

在大型应用程序中,测试用例的数量可能会多到难以应付,因此让程序处理测试结果序列是非常有用的。testrepository包(https://pypi.python.org/pypi/testrepository)目的就是解决这一问题,它提供了testr程序,可以用来处理要运行的测试数据库。

$testr init
$ touch .testr.conf
% python -m subunit.run test_scenario | testr load
Ran 4 tests in 0.001s
PASSED (id=0)
$ testr failing
PASSED (id=0)
$ testr last
Ran 3 tests in 0.001s
PASSED (id=0)
$ testr slowest
Test id                                         Runtime (s)

----------------------------------------------  -----------
test_python_status_code_handling(Not found)  0.000
test_python_status_code_handling(Server error)  0.000
test_python_status_code_handling(Client error)  0.000
$ testr stats
runs=1

一旦subunit的测试流被运行并加载到testrepository,接下来就很容易使用testr命令来操作了。

显然,每次手工处理要运行的测试是很烦人的。因此,应该“教会”testr如何执行要运行的测试,以便它可以自己去加载测试结果。这可以通过编辑项目的根目录中的.testr.conf文件(见示例6.13)来实现。

示例6.13 .testr.conf文件

[DEFAULT]  
test_command=python -m subunit.run discover . $LISTOPT $IDOPTION         1
test_id_option=--load-list $IDFILE             2
test_list_option=--list                  3

1 执行testr run时要运行的命令。
2 加载测试列表要运行的命令。
3 列出测试要运行的命令。

第一行的test_command是最关键的。现在只需要运行testr run就可以将测试加载到testrepository中并执行。

 注意

  如果习惯用noseteststestr run现在是等效的命令。

另外两个选项可以支持测试的并行运行。通过给testr run加上--prallel选项即可轻松实现,如示例6.14所示。并行运行测试可以极大地加速测试过程。

示例6.14 运行testr run --parallel

$ testr run --parallel
running=python -m subunit.run discover .  --list
running=python -m subunit.run discover .  --load-list /tmp/tmpiMq5Q1
running=python -m subunit.run discover .  --load-list /tmp/tmp7hYEkP
running=python -m subunit.run discover .  --load-list /tmp/tmpP_9zBc
running=python -m subunit.run discover .  --load-list /tmp/tmpTejc5J
Ran 26 (+10) tests in 0.029s (-0.001s)
PASSED (id=7, skips=3)

在后台,testr运行测试列出操作,然后将测试列表分成几个子列表,并分别创建Python进程运行测试的每个子列表。默认情况下,子列表的数量与当前使用的机器的CPU数目相等。可以通过加入--concurrency标志设置进程的数目。

$ testr run --parallel --concurrency=2

可以想象,类似subunittestrepository这样的工具将为测试效率的提升带来更多可能,而本节只是一个大概介绍。熟悉这些工具是非常值得的,因为测试会极大地影响你将要开发和发布的软件的质量。利用这些有力的工具能够节省很多时间。

testrepository也可以同setuptools集成,并且为其部署testr命令。这使得与基于setup.py工作流的集成更加容易,例如,可以围绕setup.py记录整个项目。setup.py testr命令可以接受一些选项,如--testr-args(通过它可以为testr加入更多选项)或者--coverage(这将在下一节介绍)。


1甚至可以支持来自不同源程序或语言的测试结果。