软件开发公司,本文将进入单元测试的部分,这也是基础知识中较后一个大块。本文将重点讲述Python和OpenStack中的单元测试的生态环境。
通过demo学习OpenStack开发——单元测试
单元测试的重要性
单元测试工具
unittest
mock
testtools
fixtures
testscenarios
subunit
testrepository
coverage
tox
单元测试工具小结
Keystone的单元测试框架
使用tox进行测试环境管理
使用testrepository管理测试的运行
单元测试用例的代码架构
总结
系列后记
单元测试的重要性
GitHub上有个人画了一些不同语言的学习曲线图:Learning Curves (for different programming languages),虽然有些恶搞的倾向,不过确实说明了问题。这里贴一下Python的部分:
这个图说明了,会单元测试对于提高Python生产力的重要性,这主要是因为Python是个动态语言,很多问题都无法通过静态编译检查来发现,因此单元测试就成了一个重要的确保质量的手段。OpenStack的核心项目都对单元测试有极高的要求,以保证项目的高质量。
单元测试工具
Python的单元测试工具很多,为单元测试提供不同方面的功能。OpenStack的项目也基本把现在流行的单元测试工具都用全了。单元测试可以说是入门OpenStack开发的较难的部分,也是较后一公里。本章,我们就介绍一下在OpenStack中会用到的单元测试的工具。由于数量很多,不可能详细介绍,因此主要做一些概念和用途上的介绍。
unittest
unittest是Python的标准库,提供了较基本的单元测试功能,包括单元测试运行器(简称runner)和单元测试框架。项目的单元测试代码的测试类可以继承unittest.TestCase类,这样这个类就能够被runner发现并且执行。同时,unittest.TestCase这个类还定义了setUp(),tearDown(),setUpClass()和tearDownClass()方法,是用来运行单元测试前的设置工作代码和单元测试后的清理工作代码,这个也是所有Python代码遵守的规范,所以第三方的单元测试库和框架也都遵循这个规范。
unittest库也提供了一个runner,可以使用$ python -m unittest test_module的命令来执行某个模块的单元测试。另外,在Python中指定要运行的单元测试用例的完整语法是:path.to.your.module:ClassOfYourTest.test_method。
unittest是学习Python单元测试较基本也较重要的一个库,完整的说明请查看官方文档。
mock
mock也是另一个重要的单元测试库,在Python 2中是作为一个第三方库被使用的,到Python 3时,就被纳入了标准库,可见这个库的重要性。简单的说,mock就是用来模拟对象的行为,这样在进行单元测试的时候,可以指定任何对象的返回值,便于测试对外部接口有依赖的代码。关于mock的使用,可以查看我之前写的这篇文章Python Mock的入门。
testtools
testtools是个unittest的扩展框架,主要是在unittest的基础上提供了更好的assert功能,使得写单元测试更加方便。具体可以查看文档。
fixtures
fixture的意思是固定装置,在Python的单元测试中,是指某段可以复用的单元测试setUp和tearDown代码组合。一个fixture一般用来实现某个组件的setUp和tearDown逻辑,比如测试前要先创建好某些数据,测试后要删掉这些数据,这些操作就可以封装到一个fixture中。这样不同的测试用例就不用重复写这些代码,只要使用fixture即可。fixtures模块是一个第三方模块,提供了一种简单的创建fixture类和对象的机制,并且也提供了一些内置的fixture。具体的使用方法可以查看官方文档。
testscenarios
testscenarios模块满足了场景测试的需求。它的基本用法是在测试类中添加一个类属性scenarios,该属性是一个元组,定义了每一种场景下不同的变量的值。比如说你测试一段数据访问代码,你需要测试该代码在使用不同的驱动时,比如MongoDB、SQL、File,是否都能正常工作。我们有三种办法:
较笨的办法是为不同的驱动把同一个测试用例编写3遍。
比较好的办法是,编写一个统一的非测试用例方法,接收driver作为参数,执行测试逻辑,然后再分别编写三个测试用例方法去调用这个非测试用例方法。
更好的办法就是使用testscenarios模块,定义好scenarios变量,然后实现一个测试用例方法。
testscenarios模块在OpenStack Ceilometer中被大量使用。更多的信息可以查看文档。
subunit
subunit是一个用于传输单元测试结果的流协议。一般来说,运行单元测试的时候是把单元测试的结果直接输出到标准输出,但是如果运行大量的测试用例,这些测试结果就很难被分析。因此就可以使用python-subunit模块来运行测试用例,并且把测试用例通过subunit协议输出,这样测试结果就可以被分析工具聚合以及分析。python-subunit模块自带了一些工具用来解析subunit协议,比如你可以这样运行测试用例:$ python -m subunit.run test_module | subunit2pyunit,subunit2pyunit命令会解析subunit协议,并且输出到标准输出。关于subunit的更多信息,请查看官方文档。
testrepository
OpenStack中使用testrepository模块管理单元测试用例。当一个项目中的测试用例很多时,如何更有效的处理单元测试用例的结果就变得很重要。testrepository的出现就是为了解决这个问题。testrepository使用python-subunit模块来运行测试用例,然后分析subunit的输出并对测试结果进行记录(记录到本地文件)。举例来说,testrepository允许你做这样的事情:
知道哪些用例运行时间较长
显示运行失败的用例
重新运行上次运行失败的用例
testrepository的更多信息,请查看官方文档。
coverage
coverage是用来计算代码运行时的覆盖率的,也就是统计多少代码被执行了。它可以和testrepository一起使用,用来统计单元测试的覆盖率,在运行完单元测试之后,输出覆盖率报告。具体的使用方法可以查看官方文档。
tox
tox是用来管理和构建虚拟环境(virtualenv)的。对于一个项目,我们需要运行Python 2.7的单元测试,也需要运行Python 3.4的单元测试,还需要运行PEP8的代码检查。这些不同的任务需要依赖不同的库,所以需要使用不同的虚拟环境。使用tox的时候,我们会在tox的配置文件tox.ini中指定不同任务的虚拟环境名称,该任务在虚拟环境中需要安装哪些包,以及该任务执行的时候需要运行哪些命令。更多信息,请查看官方文档。
单元测试工具小结
本章介绍了OpenStack中常用的单元测试工具的基本用途,希望大家对这些工具有个大概的认识。这里我们可以按照类别总结一下这些工具:
测试环境管理: tox
使用tox来管理测试运行的虚拟环境,并且调用testrepository来执行测试用例。
测试用例的运行和管理: testrepository, subunit, coverage
testrepository调用subunit来执行测试用例,对测试结果进行聚合和管理;调用coverage来执行代码覆盖率的计算。
测试用例的编写: unittest, mock, testtools, fixtures, testscenarios
使用testtools作为所有测试用例的基类,同时应用mock, fixtures, testscenarios来更好的编写测试用例。
在The Hacker's Guide to Python(《Python高手之路》)一书中,也有专门的一章介绍了各种单元测试工具及其用法,读者也可以参考一下。下一章,我们来分析Keystone项目的单元测试框架,可以让你看到在OpenStack的实际项目中,这些工具是如何被使用的。