单元测试老生常谈了,但是测试要会单元测试还是近几年在国内流行起来的,测试开发岗位那是必备技能了,所以这个单元测试还是要接触下,就算以后的岗位中用不上,但万一面试时问到单元测试,也不至于太狼狈,或者学到这个技能不是不可以往测试开发岗位发展下。我们看下python怎么实现单元测试。
python的优点想必大家已经很了解了,现在想到要单元测试,那么肯定有牛人已经探好路子,并研发了这个模块,我们用就好了,没错,就是unittest模块:
TestCase 测试用例,就是功能里那样一条条用例
TestSuite 多个测试用例集合在一起,就是TestSuite,就是一个功能模块的所有用例放这里了
TestLoader是用来加载TestCase到TestSuite中的,这个方法很好玩,可以将一个目录下的所有python文件里的测试用例抠出来
TestRunner是来执行测试用例的,测试的结果会保存到TestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息
1.unittest测试的基本方法
1 import unittest #导入unittest模块 2 import HTMLTestRunner,xmlrunner #导入生成报告的模块,html生成的是方便给自己看的,xml生成的报告是以后给jekens用的,能自动识别测试报告内容。 3 class MyTest(unittest.TestCase): #定义一个测试用例的类 4 def testa(self): #定义一个测试用例,注意,测试用例必须test开始,不然不会当做是测试用例 5 '''a''' #描述,会同测试用例标题一并显示在测试报告里 6 self.assertEqual(1,1) #测试用例断言,比较预期结果与实际结果,这里1==1,显然结果是pass 7 def testhaha(self): 8 '''b''' 9 self.assertEqual(2, 1) #测试用例断言,比较预期结果与实际结果,这里2==1,显然结果是Fail 10 def testb(self): 11 '''c''' 12 self.assertEqual(3, 2) #测试用例断言,比较预期结果与实际结果,这里3==1,显然结果是Fail 13 14 class MyTest2(unittest.TestCase): 15 def testc(self): 16 '''d''' 17 self.assertEqual(1,1) 18 def testhaha(self): 19 '''e''' 20 self.assertEqual(2, 1) 21 def testd(self): 22 '''f''' 23 self.assertEqual(3, 2) 24 25 if __name__=='__main__': 26 # unittest.main() #运行所有的测试用例,运行下面的代码时这个要注释 27 suite=unittest.TestSuite() #定义一个测试套件 28 # suite.addTest(MyTest('testa')) #添加测试用例方法一:往测试套件里新增一条测试用例,测试用例只能用一种方法,同时两种会报错 29 # suite.addTest(MyTest('testb')) 30 suite.addTest(unittest.makeSuite(MyTest)) #添加测试用例方法二:往测试套件里新增这个类下的所有测试用例 31 suite.addTest(unittest.makeSuite(MyTest2)) 32 # fw=open('test.html','wb') #定义一个文件对象,给后面的HTMLTestRunner生成测试报告用,注意打开方式必须是wb 33 # runner=HTMLTestRunner.HTMLTestRunner(stream=fw,title='Testing',description='miaoshu') #生成测试报告方法一:HTMLTestRunner,需要指定文件对象和标题 34 runner=xmlrunner.XMLTestRunner(output='.') #生成测试报告方法二:xmlrunner,这个方法只要指定测试报告目录就可以 35 runner.run(suite) #运行,注意上述方法我写一起了,运行的话只能运行一种,另一个要注释
2.unittest中的setUp和tearDown的应用
1 #setUp和tearDown的运用 2 import unittest 3 class MyTest(unittest.TestCase): 4 @classmethod 5 def setUpClass(cls): #类开始前运行,比如在执行这些用例之前需要备份数据库 6 print('1') 7 8 @classmethod 9 def tearDownClass(cls): #类结束后运行,比如在执行这些用例之后需要还原数据库 10 print('0') 11 def setUp(self): #测试用例执行前运行 12 print('a') 13 def tearDown(self): #测试用例执行后运行 14 print('z') 15 def testa(self): 16 print('测试用例1') 17 self.assertEqual(1,1) 18 def testb(self): 19 print('测试用例2') 20 self.assertEqual(1,1) 21 if __name__=='__main__': 22 unittest.main()
3.unittest中的参数化
参数化的概念不陌生了,一种是需要大量数据还有一种是有唯一性校验的情况都需要参数化,那么在unittest中怎么参数化呢?如下:
1 import unittest,HTMLTestRunner 2 from nose_parameterized import parameterized 3 def login(username,passwd): 4 if username=='xiaogang' and passwd=='123456': 5 return True 6 else: 7 return False 8 9 class Login(unittest.TestCase): 10 @parameterized.expand( 11 [ 12 ['xiaogang','123456',True], #可以是list,也可以是元祖 13 ['','123456',True], 14 ['xiaogang','',False], 15 ['adgadg','123456',False] 16 ] 17 ) 18 def test_login(self,username,passwd,exception): #这里的参数对应上述列表里的元素,运行的时候会遍历上述列表里的二维列表直到所有元素都调用运行完成 19 '''登录''' 20 res=login(username,passwd) 21 self.assertEqual(res,exception) 22 23 if __name__=='_main__': 24 suite = unittest.TestSuite() 25 suite.addTest(unittest.makeSuite(Login)) 26 fw = open('a.html', 'wb') 27 runner = HTMLTestRunner.HTMLTestRunner( 28 stream=fw, title='参数化测试数据', description='描述' 29 ) 30 runner.run(suite)
4.unittest参数化文件
上面的参数化要自己手写一个二维数组,但是要做一个测试框架,显然不应该这样low,用框架的人直接维护问文档就好了啊,其实比较简单,代码如下:
1 #用文件作为参数进行unittest单元测试 2 import unittest,HTMLTestRunner 3 from nose_parameterized import parameterized 4 def redCvs(filename,sep=','): 5 lis=[] 6 with open(filename,'rb') as f: #rb模式,在任何操作系统下打开都不会报错,考虑到系统兼容性 7 for line in f: 8 lis1=line.decode().strip().split(',') #decode是因为上面用了rb模式打开,是bytes类型,需要解码 9 lis.append(lis1) 10 return(lis) 11 12 def login(username,passwd): 13 if username=='xiaogang' and passwd=='123456': 14 return True 15 return False 16 17 class Login(unittest.TestCase): 18 @parameterized.expand(redCvs('a.txt')) 19 def testa(self,username,passwd,exception): 20 res=login(username,passwd) 21 self.assertEqual(bool(exception),res) 22 23 if __name__=='__main__': 24 suite=unittest.TestSuite() 25 suite.addTest(unittest.makeSuite(Login)) 26 fw=open('test.html','wb') 27 runner=HTMLTestRunner.HTMLTestRunner( 28 stream=fw,title='test测试',description='嘿嘿' 29 ) 30 runner.run(suite)
5.unittest处理关联接口
基本不存在单独的接口,一般都是依赖接口,比如购买商品,依赖登录接口返回的token或者session,这些都是变化的,那怎么在unittest里实现呢?代码如下:
1 #接口关联,只是一个思路,并没有结合实际的项目,有兴趣的同学可以尝试下 2 import unittest,HTMLTestRunner 3 def login(username,passwd): 4 if username=='xiaogang' and passwd=='123456': 5 return '138' 6 else: 7 return False 8 9 def shopping(sign): 10 if sign=='138': 11 return True 12 else: 13 return False 14 15 class My(unittest.TestCase): 16 def login(self): #注意,这里不以test开头命名,就是一普通方法,在执行测试用例的时候并不会运行,调用的时候才会 17 res=login('xiaogang','123456') 18 self.assertEqual(res,'138') 19 return res 20 21 def test_login_cj(self): 22 res=self.login() #调用登录方法,获取sign 23 self.jp=shopping(res) 24 self.assertEqual(self.jp,True) 25 26 suite=unittest.TestSuite() 27 suite.addTest(unittest.makeSuite(My)) 28 fw=open('res.html','wb') 29 runner=HTMLTestRunner.HTMLTestRunner( 30 stream=fw,title='关联接口',description='描述' 31 ) 32 # runner=xmlrunner.XMLTestRunner(output='.') 33 runner.run(suite)
6.unittest批量读取python文件获取所有测试用例
每个python文件里放不同的功能测试用例,方便维护,那么怎么把他们一次性运行完呢,这里就用到了unittest里的 defaultTestLoader.discover方法了,用法如下:
import unittest,HTMLTestRunner suite = unittest.TestSuite()#定义测试集合 all_case = unittest.defaultTestLoader.discover( r'E:\szg\bestTest\day11\AUTO\case','test_*.py' )#找到case目录下所以的.py文件 for case in all_case: #循环添加case到测试集合里面 suite.addTests(case) fw = open('report.html','wb') runner = HTMLTestRunner.HTMLTestRunner( stream=fw,title='多个文件运行' ) runner.run(suite)