概述
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
类似的概念还有QA测试,二者存在着一些区别,具体如下:
QA测试 | 单元测试 | |
---|---|---|
时间 | 开发后 | 开发中、开发前(测试驱动开发) |
对象 | 模块、(子)系统 | 函数、类 |
方式 | 手动or自动 | 自动 |
测试目的 | 功能、异常、性能、压力 | 算法、逻辑 |
单元测试的意义:
- 提升代码覆盖率
- 减少调试时间
- 增强代码可维护性
- 缩短项目周期
常用的单元测试工具
unittest、Nose、Coverage.py、Mock、Pyhunter等。
unittest
python自带的单元测试框架,无需额外安装,但测试风格较为繁琐(需要定义测试类)。
Nose
第三方测试模块,易用易安装,兼容unittest中的测试类。
使用
安装好Nose后,将测试代码或测试子目录以Test*或test*命名,测试的实例函数同样以Test*或test*命名,在该目录下打开命令行执行命令nosetests即可批量测试相应命名规则的文件代码,并输出测试的相关信息。也可以指定测试某一文件或用例,命令为”nosetests xxx.py [: 用例名]”。
如批量测试以下测试代码:
# filename = test_py.py
def test_case1():
print('test_case1')
assert True
def Test_case2():
print('test_case2')
assert False
def test_case3():
print('test_case3')
assert True
在该文件目录下执行nosetests,输出为:.F.
“.”代表通过测试,F表示未通过,E表示执行中出现错误。
我们还可以在测试代码中定义setUp和tearDown两个函数,分别在测试实例开始时和测试实例结束后执行,测试代码的实际执行过程为setUp->testCases->tearDown,用于统一的资源申请和资源释放。
辅助测试工具
上例中所用的断言工具为assert,nose还提供了其他的断言工具,可通过导入nose.tools使用。首先包括丰富的断言指令:assert_almost_equal、assert_false\、assert_not_in等,用于设立不同的通过标准。其次是一些辅助工具:
timed(limit):装饰器,判断用例执行是否超时
with_setup(setup, teardown):装饰器,给某一用例增加预先执行和事后执行代码
nottest(func):普通语句,不执行一个test开头的用例
istest(func):普通语句,将一个非test开头的函数以用例形式执行
以上工具的详细信息也可以通过help(nose.tools)查看。
控制测试方式
nose提供了一些参数,来控制测试的执行和输出方式,配合nosetests使用,如:
-v:debug模式,显示具体执行情况,如用例名称
-s:开启print输出,默认不输出用例中print内容
-x:用例失败即停止,不执行后续case
–processes=NUM:多线程运行测试用例
-w:指定一个目录执行测试
可扩展性
nose本身支持其他第三方插件,大大提升了它的可扩展性,如安装NoseJS插件,结合–with-javascript即可以测试js用例。
其中一个比较重要的插件为coverage.py,它可以进行代码覆盖率的统计。可以使用nosetests -p查看是否安装了这个插件,若没有可以依照普通的python库安装方法进行安装。
安装好后,只需在测试时加上参数–with-coverage即可以生成覆盖率信息,还可以增加–cover-html生成html版测试报告。示例测试结果如下:
其中stmts代表被测试代码行数,miss代表没有测试到的代码,cover代表覆盖率((stmts-miss)/stmts),missing为具体的那些行没有被测试到。
单元测试建议
- 单测用例命名要有其测试内容的意义
- 每个用例要使用断言判断是否执行成功
- 用例间不应相互依赖、相互调用
- 单一用例只测单一函数的单一行为