一:单元测试
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
比如对函数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 14 >>> d.a5 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_test2 .....3 ----------------------------------------------------------------------4 Ran 5 tests in 0.000s5 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 10010 >>> d1.y = 20011 >>> d1['y']12 20013 >>> d2 = Dict(a=1, b=2, c='3')14 >>> d2.c15 '3'16 >>> d2['empty']17 Traceback (most recent call last):18 ...19 KeyError: 'empty'20 >>> d2.empty21 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] = value36 37 if __name__=='__main__':38 import doctest39 doctest.testmod()
运行python mydict2.py
:
1 $ python mydict2.py
什么输出也没有。这说明我们编写的doctest运行都是正确的。
如你想要拥有完美无暇的友谊,可能一辈子找不到朋友