一:单元测试
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
比如对函数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运行都是正确的。