背景
接上一节的弱密码例子,我们的用例尽管运行的不错,但还是有点问题。
假如我们需要增加一些测试数据,那么我们就必须去修改setUp
方法,在test_data
列表中增加数据,频繁修改代码以适应一些不变的测试场景,这是没有必要的开销,可以想办法去优化(代码与数据分离的思想)。
我们可以把测试数据保存在文件里,通过读取文件的方式,每次动态从测试文件中读取数据,这样数据的改变并不会影响测试代码,代码逻辑相对稳定,维护成本得到一定的降低。
设计测试数据
我们可以把测试数据保存成json格式,json格式的数据在各个语言间有较好的通用性,比较适合复用。
新建user_data.json文件,内容如下
[
{"name":"jack","password":"Iloverose"},
{"name":"rose","password":"Ilovejack"},
{"name":"tom","password":"password123"}
]
使用python的json库解析上面的json文件,可以得到如上节中test_data
一致的数据。
代码
新建test_password_2.py
,内容如下
import unittest
import json
class PasswordWithJsonTestCase(unittest.TestCase):
data_file_path = './user_data.json'
def setUp(self):
print('set up')
self.test_data = json.loads(open(self.data_file_path).read())
def test_weak_password(self):
for data in self.test_data:
passwd = data['password']
self.assertTrue(len(passwd) >= 6)
msg = "user %s has a weak password" %(data['name'])
self.assertTrue(passwd != 'password', msg)
self.assertTrue(passwd != 'password123', msg)
def test_dummy(self):
pass
if __name__ == '__main__':
unittest.main()
跟上一节相比,最大的不同点是现在test_data通过解析json文件的方式来赋值self.test_data = json.loads(open(self.data_file_path).read())
。
执行测试文件,结果应该与上一节一致。
发现问题
上面的代码有2个测试方法:test_week_password
和test_dummy
。由于setUp
会在每个测试方法执行之前执行一次,那么setUp方法会执行2次,相应的json文件也会读取2次。如果测试方法多的话,那么反复读取json文件对性能来说是一个巨大的挑战。
优化
对于上面的测试数据读取场景,我们可以在所有测试方法执行前读取一次数据,毕竟测试数据在所有测试方法执行过程中是保持不变的。
setUpClass()和tearDownClass()
- setUpClass方法在每个测试用例类执行之前会执行一次,接收该class作为唯一的参数,并且必须使用装饰器classmethod()
- tearDownClass: 在所有测试方法执行完之后被调用1次,调用方式跟上面的方法类似
重构
下面我们重构代码以达到只读取1次测试数据的目的,新建文件`test_password_3.py
,内容如下
import unittest
import json
class WeakPasswordTestCase(unittest.TestCase):
@classmethod
def setUpClass(kls):
data_file_path = './user_data.json'
print('before all test methods')
with open(data_file_path) as f:
kls.test_data = json.loads(f.read())
def test_weak_password(self):
for data in self.test_data: #通过self可以访问类的成员吗?需要验证一下
passwd = data['password']
self.assertTrue(len(passwd) >= 6)
msg = "user %s has a weak password" %(data['name'])
self.assertTrue(passwd != 'password', msg)
self.assertTrue(passwd != 'password123', msg)
def test_dummy(self):
pass
if __name__ == '__main__':
unittest.main()
有几点需要提及一下
- 使用
open
方法的with模式可以在读取文件后自动关闭文件 - 在setUpClass方法中可以直接设置变量,比如
kls.test_data = json.loads(f.read())
,在其他测试方法中可以被访问
本文引入了测试数据和测试脚本分离的思想并给出了实现方法,该思想很重要。