Python day 9(7) 测试
一:单元测试
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
比如对函数abs()
,我们可以编写出以下几个测试用例:
-
输入正数,比如
1
、1.2
、0.99
,期待返回值与输入相同; -
输入负数,比如
-1
、-1.2
、-0.99
,期待返回值与输入相反; -
输入
0
,期待返回0
; -
输入非数值类型,比如
None
、[]
、{}
,期待抛出TypeError
。
把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。
如果单元测试通过,说明我们测试的这个函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。
我们来编写一个Dict
类,这个类的行为和dict
一致,但是可以通过属性来访问,用起来就像下面这样:
1 >>> d = Dict(a=1, b=2) 2 >>> d['a'] 3 1 4 >>> d.a 5 1
mydict.py
代码如下:
1 class Dict(dict): 2 3 def __init__(self, **kw): 4 super().__init__(**kw) 5 6 def __getattr__(self, key): 7 try: 8 return self[key] 9 except KeyError: 10 raise AttributeError(r"'Dict' object has no attribute '%s'" % key) 11 12 def __setattr__(self, key, value): 13 self[key] = value
为了编写单元测试,我们需要引入Python自带的unittest
模块,编写mydict_test.py
如下
1 import unittest 2 3 from mydict import Dict 4 5 class TestDict(unittest.TestCase): 6 7 def test_init(self): 8 d = Dict(a=1, b='test') 9 self.assertEqual(d.a, 1) 10 self.assertEqual(d.b, 'test') 11 self.assertTrue(isinstance(d, dict)) 12 13 def test_key(self): 14 d = Dict() 15 d['key'] = 'value' 16 self.assertEqual(d.key, 'value') 17 18 def test_attr(self): 19 d = Dict() 20 d.key = 'value' 21 self.assertTrue('key' in d) 22 self.assertEqual(d['key'], 'value') 23 24 def test_keyerror(self): 25 d = Dict() 26 with self.assertRaises(KeyError): 27 value = d['empty'] 28 29 def test_attrerror(self): 30 d = Dict() 31 with self.assertRaises(AttributeError): 32 value = d.empty
编写单元测试时,我们需要编写一个测试类,从unittest.TestCase
继承
以test
开头的方法就是测试方法,不以test
开头的方法不被认为是测试方法,测试的时候不会被执行。
编写单元测试时,我们需要编写一个测试类,从unittest.TestCase
继承。
以test
开头的方法就是测试方法,不以test
开头的方法不被认为是测试方法,测试的时候不会被执行。
对每一类测试都需要编写一个test_xxx()
方法。由于unittest.TestCase
提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。最常用的断言就是assertEqual()
1 self.assertEqual(abs(-1), 1) # 断言函数返回的结果与1相等
另一种重要的断言就是期待抛出指定类型的Error,比如通过d['empty']
访问不存在的key时,断言会抛出KeyError
:
1 with self.assertRaises(KeyError): 2 value = d['empty']
而通过d.empty
访问不存在的key时,我们期待抛出AttributeError
:
1 with self.assertRaises(AttributeError): 2 value = d.empty
二:运行单元测试
法一:
最简单的运行方式是在mydict_test.py
的最后加上两行代码:
1 if __name__ == '__main__': 2 unittest.main()
这样就可以把mydict_test.py
当做正常的python脚本运行:
1 $ python mydict_test.py
法二:(推荐此法)
在命令行通过参数-m unittest
直接运行单元测试
1 $ python -m unittest mydict_test 2 ..... 3 ---------------------------------------------------------------------- 4 Ran 5 tests in 0.000s 5 6 OK
三:setUp与tearDown
可以在单元测试中编写两个特殊的setUp()
和tearDown()
方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。
setUp()
和tearDown()
方法有什么用呢?设想你的测试需要启动一个数据库,这时,就可以在setUp()
方法中连接数据库,在tearDown()
方法中关闭数据库,这样,不必在每个测试方法中重复相同的代码:
1 class TestDict(unittest.TestCase): 2 3 def setUp(self): 4 print('setUp...') 5 6 def tearDown(self): 7 print('tearDown...')
四:文档测试 (Python内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。)
1 # mydict2.py 2 class Dict(dict): 3 ''' 4 Simple dict but also support access as x.y style. 5 6 >>> d1 = Dict() 7 >>> d1['x'] = 100 8 >>> d1.x 9 100 10 >>> d1.y = 200 11 >>> d1['y'] 12 200 13 >>> d2 = Dict(a=1, b=2, c='3') 14 >>> d2.c 15 '3' 16 >>> d2['empty'] 17 Traceback (most recent call last): 18 ... 19 KeyError: 'empty' 20 >>> d2.empty 21 Traceback (most recent call last): 22 ... 23 AttributeError: 'Dict' object has no attribute 'empty' 24 ''' 25 def __init__(self, **kw): 26 super(Dict, self).__init__(**kw) 27 28 def __getattr__(self, key): 29 try: 30 return self[key] 31 except KeyError: 32 raise AttributeError(r"'Dict' object has no attribute '%s'" % key) 33 34 def __setattr__(self, key, value): 35 self[key] = value 36 37 if __name__=='__main__': 38 import doctest 39 doctest.testmod()
运行python mydict2.py
:
1 $ python mydict2.py
什么输出也没有。这说明我们编写的doctest运行都是正确的。