单元测试
单元测试简介
单元测试是用来对一个模块、一个函数或者一个类进行正确性检验的测试工作。
比如对函数abs()
,我们可以编写以下几个测试用例:
- 输入正数,比如
1
,9.8
,0.99999
,期待返回值与输入值相同; - 输入负数,比如
-1
,-9.8
,-0.99999
,期待返回值与输入相反; - 输入
0
,期待返回0
; - 输入非数值类型,比如
None
,[]
,{}
,hello
,期待抛出TypeError
把上面的测试用例放到一个测试模块里,就是一个完整的单元测试,如果单元测试通过,说明我们测试的函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。
## 单元测试的意义 如果我们对abs()函数代码进行了修改,只需要再跑一遍单元测试,如果通过,说明我们的修改不会对函数原有的行为造成影响,如果测hi是不通过,说明我们的修改与原有行为不一致,要么修改代码,要么修改测试。
这种以测试为驱动的开发模式最大的好处是确保一个程序模块的行为符合我们设计的测试用例。在将来修改的时候,我们可以极大程度地保证该模块行为仍然是正确的
## 编写单元测试 我们编写一个`Dict`类,这个类的行为和dict一致,但是可以通过属性来访问
mydict.py
class Dict(dict):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError("'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
编写单元测试可以使用引入python自带的`unittest`模块,编写mydict_test.py
mydict_test.py
import unittest
from .mydict import Dict
class TestDict(unittest.TestCase):
"""测试类"""
def test_init(self):
"""测试初始化"""
d = Dict(a=2, b="hello")
self.assertEqual(d.a, 2)
self.assertEqual(d.b, "hello")
self.assertTrue(isinstance(d, dict))
def test_key(self):
"""测试key"""
d = Dict()
d["key"] = "value"
self.assertEqual(d.key, "value")
def test_attr(self):
"""测试属性"""
d = Dict()
d.key = "value"
self.assertTrue("key" in d)
self.assertEqual(d["key"], "value")
def test_keyerror(self):
d = Dict()
with self.assertRaises(KeyError):
# 当进行下面的操作时期待抛出KeyError
value = d["empty"]
def test_attrerror(self):
d = Dict()
with self.assertRaises(AttributeError):
value = d.empty
编写单元测试需要主要以下几点:
- 需要编写一个测试类,这个类要继承
unittest.TestCase
类 - 其中以
test
开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。 - 使用断言进行条件判断,断言输出是否是我们所期望的
比如断言是否相等
self.assertEqual(abs(-1), 1) # 断言函数返回的结果与1相等
断言抛出指定类型的Error
with self.assertRaises(KeyError):
value = d['empty']
## 运行单元测试 在mydict_test.py的最后连加上如下两行代码运行 ```python if __name__ == '__main__': unittest.main()
这样就可以把mydict_test.py当作正常的python脚本运行
python mydict_test.py
```python
Ran 5 tests in 0.002s
OK
另一种方法是在命令行通过参数-m unittest直接运行单元测试: ```python python -m unittest mydict_test
这是推荐的做法,因为这样可以一次批量运行很多单元测试,并且,有很多工具可以自动来运行这些单元测试。
<br>
## setup和teardown
以在单元测试中编写两个特殊的`setUp()`和`tearDown()`方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。
setUp()和tearDown()方法有什么用呢?设想你的测试需要启动一个数据库,这时,就可以在setUp()方法中连接数据库,在tearDown()方法中关闭数据库,这样,不必在每个测试方法中重复相同的代码:
在TestDict中加上如下代码
```python
def setUp(self):
print("setUp...")
def tearDown(self):
print("tearDown...")
运行结果
setUp...
tearDown...
setUp...
tearDown...
setUp...
tearDown...
setUp...
tearDown...
setUp...
tearDown...