摘要:本文将进入单元测试的部分,这也是基础知识中最后一个大块。本文将重点讲述和中的单元测试的生态环境。另外,在中指定要运行的单元测试用例的完整语法是。中使用模块管理单元测试用例。每个项目的单元测试代码结构可
本文将进入单元测试的部分,这也是基础知识中最后一个大块。本文将重点讲述Python和OpenStack中的单元测试的生态环境。
单元测试的重要性github上有个人画了一些不同语言的学习曲线图:Learning Curves (for different programming languages),虽然有些恶搞的倾向,不过确实说明了问题。这里贴一下Python的部分:
这个图说明了,会单元测试对于提高Python生产力的重要性,这主要是因为Python是个动态语言,很多问题都无法通过静态编译检查来发现,因此单元测试就成了一个重要的确保质量的手段。OpenStack的核心项目都对单元测试有极高的要求,以保证项目的高质量。
单元测试工具Python的单元测试工具很多,为单元测试提供不同方面的功能。OpenStack的项目也基本把现在流行的单元测试工具都用全了。单元测试可以说是入门OpenStack开发的最难的部分,也是最后一公里。本章,我们就介绍一下在OpenStack中会用到的单元测试的工具。由于数量很多,不可能详细介绍,因此主要做一些概念和用途上的介绍。
unittestunittest是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单元测试最基本也最重要的一个库,完整的说明请查看官方文档:https://docs.python.org/2.7/library/unittest.html。
mockmock也是另一个重要的单元测试库,在Python 2中是作为一个第三方库被使用的,到Python 3时,就被纳入了标准库,可见这个库的重要性。简单的说,mock就是用来模拟对象的行为,这样在进行单元测试的时候,可以指定任何对象的返回值,便于测试对外部接口有依赖的代码。关于mock的使用,可以查看我之前写的这篇文章Python Mock的入门
testtoolstesttools是个unittest的扩展框架,主要是在unittest的基础上提供了更好的assert功能,使得写单元测试更加方便。具体可以查看文档:http://testtools.readthedocs.org/en/latest/。
fixturesfixture的意思是固定装置,在Python的单元测试中,是指某段可以复用的单元测试setUp和tearDown代码组合。一个fixture一般用来实现某个组件的setUp和tearDown逻辑,比如测试前要先创建好某些数据,测试后要删掉这些数据,这些操作就可以封装到一个fixture中。这样不同的测试用例就不用重复写这些代码,只要使用fixture即可。fixtures模块是一个第三方模块,提供了一种简单的创建fixture类和对象的机制,并且也提供了一些内置的fixture。具体的使用方法可以查看官方文档:https://pypi.python.org/pypi/fixtures/。
testscenariostestscenarios模块满足了场景测试的需求。它的基本用法是在测试类中添加一个类属性scenarios,该属性是一个元组,定义了每一种场景下不同的变量的值。比如说你测试一段数据访问代码,你需要测试该代码在使用不同的驱动时,比如MongoDB、SQL、File,是否都能正常工作。我们有三种办法:
最笨的办法是为不同的驱动把同一个测试用例编写3遍。
比较好的办法是,编写一个统一的非测试用例方法,接收driver作为参数,执行测试逻辑,然后再分别编写三个测试用例方法去调用这个非测试用例方法。
更好的办法就是使用testscenarios模块,定义好scenarios变量,然后实现一个测试用例方法。
testscenarios模块在OpenStack Ceilometer中被大量使用。更多的信息可以查看文档:https://pypi.python.org/pypi/testscenarios/
subunitsubunit是一个用于传输单元测试结果的流协议。一般来说,运行单元测试的时候是把单元测试的结果直接输出到标准输出,但是如果运行大量的测试用例,这些测试结果就很难被分析。因此就可以使用python-subunit模块来运行测试用例,并且把测试用例通过subunit协议输出,这样测试结果就可以被分析工具聚合以及分析。python-subunit模块自带了一些工具用来解析subunit协议,比如你可以这样运行测试用例:$ python -m subunit.run test_module | subunit2pyunit,subunit2pyunit命令会解析subunit协议,并且输出到标准输出。关于subunit的更多信息,请查看官方文档:https://pypi.python.org/pypi/python-subunit/。
testrepositoryOpenStack中使用testrepository模块管理单元测试用例。当一个项目中的测试用例很多时,如何更有效的处理单元测试用例的结果就变得很重要。testrepository的出现就是为了解决这个问题。testrepository使用python-subunit模块来运行测试用例,然后分析subunit的输出并对测试结果进行记录(记录到本地文件)。举例来说,testrepository允许你做这样的事情:
知道哪些用例运行时间最长
显示运行失败的用例
重新运行上次运行失败的用例
testrepository的更多信息,请查看官方文档:http://testrepository.readthedocs.org/en/latest/。
coveragecoverage是用来计算代码运行时的覆盖率的,也就是统计多少代码被执行了。它可以和testrepository一起使用,用来统计单元测试的覆盖率,在运行完单元测试之后,输出覆盖率报告。具体的使用方法可以查看官方文档:http://coverage.readthedocs.org/en/latest/。
toxtox是用来管理和构建虚拟环境(virtualenv)的。对于一个项目,我们需要运行Python 2.7的单元测试,也需要运行Python 3.4的单元测试,还需要运行PEP8的代码检查。这些不同的任务需要依赖不同的库,所以需要使用不同的虚拟环境。使用tox的时候,我们会在tox的配置文件tox.ini中指定不同任务的虚拟环境名称,该任务在虚拟环境中需要安装哪些包,以及该任务执行的时候需要运行哪些命令。更多信息,请查看官方文档:https://testrun.org/tox/latest/
单元测试工具小结本章介绍了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的实际项目中,这些工具是如何被使用的。
Keystone的单元测试框架现在,我们以Keystone项目为例,来看下真实项目中的单元测试是如何架构的。我们采用自顶向下的方式,先从最上层的部分介绍起。
使用tox进行测试环境管理大部分情况下,我们都是通过tox命令来执行单元测试的,并且传递环境名称给tox命令:
➜ ~/openstack/env/p/keystone git:(master) ✗ $ tox -e py27
tox命令首先会读取项目根目录下的tox.ini文件,获取相关的信息,然后根据配置构建virtualenv,保存在.tox/目录下,以环境名称命名:
➜ ~/openstack/env/p/keystone git:(master) ✗ $ ls .tox log pep8 py27
除了log目录,其他的都是普通的virtualenv环境,你可以自己查看一下内容。我们来看下py27这个环境的相关配置(在tox.ini)中,我直接在内容上注释一些配置的用途:
[tox] minversion = 1.6 skipsdist = True # envlist表示本文件中配置的环境都有哪些 envlist = py34,py27,pep8,docs,genconfig,releasenotes # testenv是默认配置,如果某个配置在环境专属的section中没有,就从这个section中读取 [testenv] # usedevelop表示安装virtualenv的时候,本项目自己的代码采用开发模式安装,也就是不会拷贝代码到virtualenv目录中,只是做个链接 usedevelop = True # install_command表示构建环境的时候要执行的命令,一般是使用pip安装 install_command = pip install -U {opts} {packages} setenv = VIRTUAL_ENV={envdir} # deps指定构建环境的时候需要安装的依赖包,这个就是作为pip命令的参数 # keystone这里使用的写法比较特殊一点,第二行的.[ldap,memcache,mongodb]是两个依赖,第一个点"."表示当前项目的依赖,也就是requirements.txt,第二个部分[ldap,memcache,mongodb]表示extra,是在setup.cfg文件中定义的一个段的名称,该段下定义了额外的依赖,这些可以查看PEP0508 # 一般的项目这里会采用更简单的方式来书写,直接安装两个文件中的依赖: # -r{toxinidir}/requirements.txt # -r{toxinidir}/test-requirements.txt deps = -r{toxinidir}/test-requirements.txt .[ldap,memcache,mongodb] # commands表示构建好virtualenv之后要执行的命令,这里调用了tools/pretty_tox.sh来执行测试 commands = find keystone -type f -name "*.pyc" -delete bash tools/pretty_tox.sh "{posargs}" whitelist_externals = bash find passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY PBR_VERSION # 这个section是为py34环境定制某些配置的,没有定制的配置,从[testenv]读取 [testenv:py34] commands = find keystone -type f -name "*.pyc" -delete bash tools/pretty_tox_py3.sh
上面提到的PEP-0508是依赖格式的完整说明。setup.cfg的extra部分如下:
[extras] ldap = python-ldap>=2.4:python_version=="2.7" # PSF ldappool>=1.0:python_version=="2.7" # MPL memcache = python-memcached>=1.56 # PSF mongodb = pymongo!=3.1,>=3.0.2 # Apache-2.0 bandit = bandit>=0.17.3 # Apache-2.0使用testrepository管理测试的运行
上面我们看到tox.ini文件中的commands参数中执行的是tools/pretty_tox.sh命令。这个脚本的内容如下:
#!/usr/bin/env bash set -o pipefail TESTRARGS=$1 # testr和setuptools已经集成,所以可以通过setup.py testr命令来执行 # --testr-args表示传递给testr命令的参数,告诉testr要传递给subunit的参数 # subunit-trace是os-testr包中的命令(os-testr是OpenStack的一个项目),用来解析subunit的输出的。 python setup.py testr --testr-args="--subunit $TESTRARGS" | subunit-trace -f retval=$? # NOTE(mtreinish) The pipe above would eat the slowest display from pbr"s testr # wrapper so just manually print the slowest tests. echo -e " Slowest Tests: " # 测试结束后,让testr显示出执行时间最长的那些测试用例 testr slowest exit $retval
tox就是从tools/pretty_tox.sh这个命令开始调用testr来执行单元测试的。testr本身的配置是放在项目根目录下的.testr.conf文件:
[DEFAULT] test_command= ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./keystone/tests/unit} $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list group_regex=.*(test_cert_setup) # NOTE(morganfainberg): If single-worker mode is wanted (e.g. for live tests) # the environment variable ``TEST_RUN_CONCURRENCY`` should be set to ``1``. If # a non-default (1 worker per available core) concurrency is desired, set # environment variable ``TEST_RUN_CONCURRENCY`` to the desired number of # workers. test_run_concurrency=echo ${TEST_RUN_CONCURRENCY:-0}
这个文件中的配置项可以从testr官方文档中找到。其中test_command命令表示要执行什么命令来运行测试用例,这里使用的是subunit.run,这个我们在上面提到过了。
到目前为止的流程就是:
tox建好virtualenv
tox调用testr
testr调用subunit来执行测试用例
每个OpenStack项目基本上也都是这样。如果你自己在开发一个Python项目,你也可以参考这个架构。
单元测试用例的代码架构下面我们来看一下Keystone的单元测试代码是如何写的,主要是看一下其层次结构。每个OpenStack项目的单元测试代码结构可能都不一样,不过你了解完Keystone的结构之后,看其他项目的就会比较快了。
我们以一个测试类为例来分析测试代码的结构:keystone.tests.unit.test_v3_assignment:AssignmentTestCase。下面是这个类的继承结构,同一级别的缩进表示多重继承,增加缩进表示父类,这里删掉了不必要的路径前缀(从unit目录开始),如下所示:
# 这个测试类是测RoleAssignment的API的 unit.test_v3_assignment.RoleAssignmentBaseTestCase -> unit.test_v3.AssignmentTestMixin 这个类包含了一下测试Assignment的工具函数 -> unit.test_v3.RestfulTestCase 这个类是进行V3 REST API测试的基类,实现了V3 API的请求发起和校验 -> unit.core.SQLDriverOverride 用于修改各个配置的driver字段为sql -> unit.test_v3.AuthTestMixin 包含创建认证请求的辅助函数 -> unit.rest.RestfulTestCase 这个类是进行RESP API测试的基类,V2和V3的API测试都是以这个类为基类,这个类的setUp方法会初始化数据库,创建好TestApp。 -> unit.TestCase 这个类是Keystone中所有单元测试类的基类,它主要初始化配置,以及初始化log -> unit.BaseTestCase 这个类主要是配置测试运行的基本环境,修改一些环境变量,比如HOME等。 -> oslotest.BaseTestCase 这个是在oslotest中定义的基类,原来所有的OpenStack项目的单元测试都继承自这个基类。 不过,这个继承在Keystone中已经被删除了,Keystone自己在unit.BaseTestCase中做了差不多的事情。 这个是2016-02-17做的变更,具体的可以查看这个revision 262d0b66c3bcb82eadb663910ee21ded63e77a78。 -> testtools.TestCase 使用testtools作为测试框架 -> unittest.TestCase testtools本身是unittest的扩展
从上面的层次结构可以看出,OpenStack中的大项目,由于单元测试用例很多(Keystone现在有超过6200个单元测试用例),所以其单元测试架构也会比较复杂。要写好单元测试,需要先了解一下整个测试代码的架构。
总结本文我们了解了Python中的单元测试的概念和工具,并且通过Keystone项目了解了实际项目中的单元测试的架构,希望有助于各位读者更好的掌握OpenStack项目的单元测试基础。webdemo项目目前没有单元测试的代码,有兴趣的读者可以自己fork然后参考Keystone的架构为其增加完整的单元测试架构。
系列后记这个系列我打算就此结束,到目前为止一共写了8篇文章,写写停停,前后写了9个月。这里也做个小结。
一开始写这个系列的文章是因为我自己在学习OpenStack开发的过程中遇到很多困难,很难找到所需的入门文章。所以打算写点文章,既能作为自己的总结,也能为其他人提供些帮助。如果这些文章能帮到你,我就非常的开心。当然,这些文章的质量肯定有好有坏,欢迎大家提意见,如果有时间,我会继续修改。
然后,我想说一下写这类文章的难点,主要是要保证细节都是正确的,然后又不能太啰嗦。
细节都是正确的。举个例子,大学的很多数据结构教材中的代码,你直接贴到电脑上,然后编译,大部分是编译不通过的。这个会让初学者非常沮丧。所以我希望能够保证这些文章里的细节都是正确的,包括一些工具的配置,如果觉得有必要,我也会描述下配置的作用,以及要去哪里找更多的信息。如果这方面有遗漏,请和我说。
不能太啰嗦。这8篇文章里涉及的库有好几十个,每个库如果都讲仔细了,那就会让文章显得非常啰嗦。但是又不能直接让读者去看库的官方文档,所以权衡内容也是很麻烦的。如果各位有这方面的建议,也请和我说。
这个系列的文章是关于OpenStack的基础知识,其实OpenStack开发还要涉及到很多其他的知识,比如消息队列、非阻塞IO等,而且还要了解整个OpenStack的开发生态,包括Gerrit评审系统、Zuul持续集成、devstack开发环境、oslo项目等。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/37813.html
摘要:另外,项目在单元测试中使用的是的内存数据库,这样开发者运行单元测试的时候不需要安装和配置复杂的数据库,只要安装好就可以了。而且,数据库是保存在内存中的,会提高单元测试的速度。是实现层的基础。项目一般会使用数据库来运行单元测试。 OpenStack中的关系型数据库应用 OpenStack中的数据库应用主要是关系型数据库,主要使用的是MySQL数据库。当然也有一些NoSQL的应用,比如Ce...
摘要:在实际项目中,这么做肯定是不行的实际项目中不会使用内存数据库,这种数据库一般只是在单元测试中使用。接下来,我们将会了解中单元测试的相关知识。 在上一篇文章,我们介绍了SQLAlchemy的基本概念,也介绍了基本的使用流程。本文我们结合webdemo这个项目来介绍如何在项目中使用SQLAlchemy。另外,我们还会介绍数据库版本管理的概念和实践,这也是OpenStack每个项目都需要做的...
摘要:不幸的是,在软件包管理十分混乱,至少历史上十分混乱。的最大改进是将函数的参数单独放到一个的文件中这些成为包的元数据。基于的版本号管理。的版本推导这里重点说明一下基于的版本号管理这个功能。开发版本号的形式如下。 为什么写这个系列 OpenStack是目前我所知的最大最复杂的基于Python项目。整个OpenStack项目包含了数十个主要的子项目,每个子项目所用到的库也不尽相同。因此,对于...
摘要:通过,也就是通过各个项目提供的来使用各个服务的功能。通过使用的方式是由各个服务自己实现的,比如负责计算的项目实现了计算相关的,负责认证的项目实现了认证和授权相关的。的服务都是使用的方式来部署的。 使用OpenStack服务的方式 OpenStack项目作为一个IaaS平台,提供了三种使用方式: 通过Web界面,也就是通过Dashboard(面板)来使用平台上的功能。 通过命令行,也就...
摘要:到这里,我们的服务的框架已经搭建完成,并且测试服务器也跑起来了。上面的代码也就可以修改为再次运行我们的测试服务器,就可以返现返回值为格式了。我们先来完成利用来检查返回值的代码方法的第一个参数表示返回值的类型这样就完成了的返回值检查了。 上一篇文章说到,我们将以实例的形式来继续讲述这个API服务的开发知识,这里会使用Pecan和WSME两个库。 设计REST API 要开发REST AP...
阅读 1573·2021-09-23 11:21
阅读 2344·2021-09-07 10:13
阅读 834·2021-09-02 10:19
阅读 1124·2019-08-30 15:44
阅读 1719·2019-08-30 13:18
阅读 1912·2019-08-30 11:15
阅读 1104·2019-08-29 17:17
阅读 2016·2019-08-29 15:31