UnitTest——测试模块
其他相关:使用coverage工具统计python单元测试覆盖率
一、简单示例
待测类/方法 book.py
# 待测类
class Book(object):
_page = 2000
def __init__(self, bookname, author):
self.name = bookname
self.author = author
def get_page(self):
return self._page
# 待测方法
def get_name(book):
return book.name
测试类test_book.py
# 每个测试方法均以 test 开头,否则是不被unittest识别的。
class TestBook(unittest.TestCase):
def test_init(self):
print('test init()')
book = main_class.Book('bookname', 'author')
self.assertEquals(book.name, 'bookname')
self.assertEquals(book.author, 'author')
self.assertEquals(book._page, 2000)
self.assertTrue(isinstance(book, main_class.Book))
def test_get(self):
print('test get_name()')
self.assertEquals(main_class.get_name(main_class.Book('bookname', 'author')), 'bookname')
运行
if __name__ == '__main__':
unittest.main()
结果
test get_name()
.
test init()
.
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
给出的结果标识,成功是 .
,失败是 F
,出错是 E
,跳过是 S
。
测试的执行跟方法的顺序没有关系。
二、测试类
创建
- 继承unittest.TestCase。
- 以“test”开头的测试方法,每一类分别添加一个测试方法。
- 按需添加内置判断条件如:assertEquals()、assertRaises(Error)。
运行
1、pyCharm直接运行
pycharm还是比较智能的,可以直接运行测试,不过有个地方需要特别注意,pyCharm会自动识别并测试鼠标所在当前代码块,所以有可能你ctrl+shift+f10后,可能运行结果就只是测试一个方法(笔者可是让这个弄的莫名其妙,一顿不得其解)。
2、main方法
在测试文件结尾添加
if __name__ == '__main__':
unittest.main()
然后命令行运行
python xxxx.py
3、命令行运行
在命令行通过参数-m unittest
直接运行单元测试:
$ python -m unittest xxxx
这是推荐的做法,因为这样可以一次批量运行很多单元测试,并且,有很多工具可以自动来运行这些单元测试。
环境准备和尾处理
如果每个测试之前都需要准备环境如连接数据库、打开文件,或尾处理如关闭数据库、关闭数据流,不可能每个测试方法都写一遍。
setUp()
在每个测试方法运行之前运行
tearDown()
在每个测试方法运行之后运行
setUpClass()
在每个单元测试开始之前运行
tearDownClass()
在每个单元测试结束之后运行
示例:
class TestBook(unittest.TestCase):
def setUp(self):
print('setUp++++++++')
@classmethod
def setUpClass(cls):
print('setUpClass--------')
def tearDown(self):
print('tearDown++++++++++')
@classmethod
def tearDownClass(cls):
print('tearDownClass--------')
def test_init(self):
print('test init()')
def test_get(self):
print('test get_name()')
运行结果:
(UnitTest) D:\sHui\learn\UnitTest>python -m unittest test_class
setUpClass--------
setUp++++++++
test get_name()
tearDown++++++++++
.setUp++++++++
test init()
tearDown++++++++++
.tearDownClass--------
----------------------------------------------------------------------
Ran 2 tests in 0.003s
OK
跳过某case
测试跳过某个case?unittest提供了几种方法。
1、skip装饰器
- unittest.skip(reason) skip无条件跳过
- unittest.skipIf(condition, reason) skipIf当condition为True时跳过
- unittest.skipUnless(condition, reason) skipUnless当condition为False时跳过。
示例:
class TestBook(unittest.TestCase):
def test_init(self):
print('test init()')
@unittest.skip('skip this func ')
def test_get(self):
print('test get_name()')
结果:
Skipped: skip this func
test init()
Ran 2 tests in 0.002s
OK (skipped=1)
运行了两个测试,但test_get被跳过了
2、TestCase.skipTest()
测试类的skipTest()方法可以跳过当前测试方法。
class TestBook(unittest.TestCase):
def test_init(self):
print('test init()')
# 跳过此方法
def test_get(self):
self.skipTest('skip this func')
print('test get_name()')
结果同上
调整测试顺序
新建文件test_suite.py
import unittest
import test_class
if __name__ == '__main__':
suite = unittest.TestSuite()
tests = [test_class.TestBook('test_init'), test_class.TestBook('test_get')]
suite.addTests(tests)
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
输出:
test init()
test get_name()
test_init (test_class.TestBook) ... ok
test_get (test_class.TestBook) ... ok
----------------------------------------------------------------
Ran 2 tests in 0.000s
OK
三、测试输出
1、输出信息
verbosity参数可以控制执行结果的输出
-
0
是简单报告 -
1
是一般报告 -
2
是详细报告。
2、输出到文件
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(test_class.TestBook))
with open('Unittest.txt', 'a') as f:
runner = unittest.TextTestRunner(stream=f, verbosity=2)
runner.run(suite)
运行结束会在同目录下生成了Unittest.txt
3、输出为HTML
HTMLTestRunner是一个第三方的unittest HTML报告库,首先我们下载HTMLTestRunner.py,并放到当前目录下,或者你的’C:Python27Lib’下,就可以导入运行了。
- 官方原版:下载地址
- 灰蓝修改版:HTMLTestRunner.py(已调整格式,中文显示)
示例:
from HTMLTestRunner import HTMLTestRunner
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(test_class.TestBook))
with open('HTMLReport.html', 'w') as f:
runner = HTMLTestRunner(stream=f,
title='MathFunc Test Report',
description='generated by HTMLTestRunner.',
verbosity=2)
runner.run(suite)
四、Coverage的使用
1.命令行方式
详见:http://coverage.readthedocs.io/en/latest/cmd.html
可以使用help命令查看帮助:$ coverage help
关键命令如下:
A.run
执行代码覆盖率统计,只需要通过coverage的run参数执行被统计代码即可。
$ coverage run test.py arg1 arg2
test.py是测试脚本,arg1 arg2是test.py执行需要的参数。跑完后,会自动生成一个覆盖率统计结果文件(data file):.coverage。
B.report
有了覆盖率统计结果文件,只需要再运行report参数,就可以在命令里看到统计的结果。
Stmts/Miss表示语句总数/未执行到的语句数
Cover=(Stmts-Miss)/Stmts
c. html
生成html的测试报告。
$ coverage html -d covhtml
生成的报告直接关联代码,高亮显示覆盖和未覆盖的代码,支持排序。-d指定html文件夹。
2.API方式
除了使用命令行,还可以在python代码中直接调用coverage模块执行代码覆盖率的统计。使用方法也非常简单:
import coverage
cov = coverage.coverage(source = ['totest'])
cov.start()
#coding
cov.stop()
cov.report()
cov.html_report(directory='covhtml')
source指定要执行统计的文件,source = ['totest']只统计totest.py的覆盖率
directory指定生成html的路径
五、常用函数
参考文章:
廖雪峰