Python自动化之UnitTest框架实战记录
1UnitTest基本用法
UnitTest框架是Python自带的一个作为单元测试的测试框,相当于Java中的JUnit,随着自动化技术的成熟,UnitTest成为了测试框架第一选择,可以完整的结合Selenium、Requests来实现Ul和接口的自动化,由UnitTest再衍生出PyTest,PyTest可以完美结合UnitTest来实现自动化。
基本应用:
1、环境搭建,Python中已经直接加载了UnitTest框架,无须额外安装
2、四大组件:
a.testfixture:setUp(前置条件)、tearDown(后置条件),用于初始化测试用例及清理和释放资源
b.testcase:测试用例,通过集成unttest.TestCase,来实现用例的继承,在Unitfest中,测试用例都是通过test来识别的,测试用例命名test_XXX
c.testsuite:测试套件,也称之为测试用例集
d.testrunner:运行器,一般通过runner来调用suite去执行测试
UnitTest运行机制:通过在main函数中,调用unitest.main()运行所有内容
1.1UnitTest初体验
本节知识:1.对UnitTest有直观感受
讲了这么多,也完全不明白,没关系,通过实例先有个直观的了解,UnitTest是个什么东西
importunittest #通过继承unittest。TestCase来实现用例 classforTest(unittest.TestCase): #类的初始化 @classmethod defsetUpClass(cls)->None: print('class') #类的释放 @classmethod deftearDownClass(cls)->None: print('tclass') #测试用例初始化 defsetUp(self)->None: print("setUp") #测试用例释放 deftearDown(self)->None: print("teadDown") #测试用例 deftest_a(self): print("a") #测试用例 deftest_b(self): print("b") #函数 defadd(self,a,b): returna+b #测试用例 deftest_c(self): c=self.add(1,3) print('c=',c) if__name__=="__main__": unittest.main(verbosity=2) #参数verbosity=2的目的是为了让打印的信息更加完整,也可以不要
对上面的程序进行讲解:
1、类的初始化与释放
defsetUpClass(cls)->None表示类的初始化,在执行测试用例之前执行,只执行一次,函数参数为cls表示这是一个类方法
deftearDownClass(cls)->None表示类的释放,在执行测试用例之后执行,只执行一次
2、测试用例的初始化与释放
defsetUp(self)->None用于测试用例的初始化,在每个测试用例之前都会执行,有多少个测试用例,就会执行多少次
deftearDown(self)->None用于测试用例释放,在每个测试用例执行之后执行,有多少个测试用例,就会执行多少次
注意:方法setUpClass,tearDownClass,setUp,deftearDown的方法名是固定的,不能改动,不然框架无法识别
3、测试用例的定义
测试用例的命名规则为test_xxx,这样测试用例就会自动执行
注意:只有测试用例才会被执行,不以test_xxx命名的函数是方法,方法是不能被执行的
4、执行测试用例
通过在main函数中,调用unitest.main()运行所有内容
运行结果如下:
- 类的初始化方法setUpClass(cls)在所有的测试用例之前执行,类的释放函数tearDownClass(cls)在所有的测试用例之后执行
- 测试用例的初始化在每个测试用例之前都会执行,测试用例的释放在每个测试用例之后都会执行
- test_a(self)和test_b(self)是测试用例,运行时被自动执行,add(self,a,b)是函数,不会被自动执行,test_c(self)是测试用例,调用了add函数,这样就可以执行add函数了。
class setUp a teadDown setUp b teadDown setUp c=4 teadDown tclass
相信有了上面的例子,已经对UnitTest有了一个初步的印象。
下面我们进行一个实战操作
1.2UnitTest自动化实现实战
本节知识:1.自动化测试减少冗余,便于维护,2.ddt数据驱动
1、自动化测试减少冗余,便于维护
有了类的初始化与释放,测试用例的初始化与释放,我们可以将多个测试用例中相同的代码提取出来,减少自动化测试冗余,这样便于维护
下面看这样一个例子,我们打开谷歌浏览器,输入百度网址并进行搜索,搜索后关闭浏览器
#coding=utf-8 importunittest fromseleniumimportwebdriver importtime classforTest(unittest.TestCase): #测试用例初始化 #打开谷歌浏览器,并进入百度 defsetUp(self)->None: self.driver=webdriver.Chrome() self.driver.get('http://www.baidu.com') #测试用例释放 #等待3s,关闭浏览器 deftearDown(self)->None: time.sleep(3) self.driver.quit() #输入‘战狼',并点击搜索 deftest_1(self): pass self.driver.find_element_by_id('kw').send_keys('战狼') self.driver.find_element_by_id('su').click() #输入‘红海行动',并点击搜索 deftest_2(self): pass self.driver.find_element_by_id('kw').send_keys('红海行动') self.driver.find_element_by_id('su').click() if__name__=='__main__': unittest.main()
上面的案例中,我们将打开谷歌浏览器,进入百度,放在setUp中,完成每个测试用例之前的初始化,浏览器的关闭放在tearDown中,完成测试用例的释放
2UnitTest结合DDT(data-drivertests)自动化
2.1ddt中的data与unpack
在实际测试中,单个测试是需要用多种不同的条件(测试数据)对其进行测试的。
ddt中最基本的应用;在class前定义@ddt,用于表示要使用ddt了,再基于实际的应用。选择对应的装饰器来使用即可,说白了,就是一个装饰器
- data用于设定参数
- unpack用于解析参数
直接看例子比较直观
#coding=utf-8 importunittest fromddtimportddt fromddtimportdata #导入data #类之前定义装饰器,表示在类中要使用ddt了 @ddt classMyTestCase(unittest.TestCase): defsetUp(self)->None: print('{:=^20}'.format("测试开始")) deftearDown(self)->None: print("{:=^20}".format('测试结束')) #data用于设定参数 @data('战狼','哪吒','流浪地球','复仇者联盟') deftest_1(self,txt): print(txt) if__name__=='__main__': unittest.main(verbosity=2)
运行结果:
========测试开始========
战狼
========测试结束========
========测试开始========
哪吒
========测试结束========
========测试开始========
流浪地球
========测试结束========
========测试开始========
复仇者联盟
========测试结束========
可以看到测试用例deftest_1(self,txt)被执行了四次,data用于设定参数,将参数依次放入测试用例中进行测试。
我们改变一下设定的参数,将data设定的参数改为((‘战狼',‘哪吒'),(‘流浪地球',‘复仇者联盟')),再进行测试,如下所示
#coding=utf-8 importunittest fromddtimportddt fromddtimportdata #类之前定义装饰器,表示在类中要使用ddt了 @ddt classMyTestCase(unittest.TestCase): defsetUp(self)->None: print('{:=^20}'.format("测试开始")) deftearDown(self)->None: print("{:=^20}".format('测试结束')) #data用于设定参数,将包('战狼','哪吒')作为一个整体赋值给txt @data(('战狼','哪吒'),('流浪地球','复仇者联盟')) deftest_1(self,txt): print(txt) if__name__=='__main__': unittest.main(verbosity=2)
运行结果如下:
========测试开始========
('战狼','哪吒')
========测试结束========
========测试开始========
('流浪地球','复仇者联盟')
========测试结束========
可以看到,传入参数((‘战狼',‘哪吒'),(‘流浪地球',‘复仇者联盟'))时,将包(‘战狼',‘哪吒')和(‘流浪地球',‘复仇者联盟')作为一个整体,传递给测试用例了,如果我们希望将包里面的数据解开,传递给测试用例不同的参数,就需要用到unpack进行解包。
加入解包后的代码如下所示:
#coding=utf-8 importunittest fromddtimportddt fromddtimportdata fromddtimportunpack#导入unpack #类之前定义装饰器,表示在类中要使用ddt了 @ddt classMyTestCase(unittest.TestCase): defsetUp(self)->None: print('{:=^20}'.format("测试开始")) deftearDown(self)->None: print("{:=^20}".format('测试结束')) @data(('战狼','哪吒'),('流浪地球','复仇者联盟')) #解包,将('战狼','哪吒')解包,'战狼'赋值给txt1,'哪吒'赋值给txt2 @unpack deftest_3(self,txt1,txt2): print(txt1) print(txt2) if__name__=='__main__': unittest.main(verbosity=2)
执行结果如下:
========测试开始========
战狼
哪吒
========测试结束========
========测试开始========
流浪地球
复仇者联盟
========测试结束========
可以看到,unpack对每次传入的包进行解包,例如将(‘战狼',‘哪吒')解包,‘战狼'赋值给txt1,'哪吒'赋值给txt2
上面的例子中,我们将输入的参数直接固定了,其实也可以通过文件读取,读取结果决定
#coding=utf-8 importunittest fromddtimportddt fromddtimportdata fromddtimportunpack defreadFile(): params=[] file=open('ddt.txt','r',encoding='gbk') forlineinfile.readlines(): params.append(line.strip('\n').split(',')) returnparams #类之前定义装饰器,表示在类中要使用ddt了 @ddt classMyTestCase(unittest.TestCase): defsetUp(self)->None: print('{:=^20}'.format("测试开始")) deftearDown(self)->None: print("{:=^20}".format('测试结束')) #从文件中读取 @data(*readFile()) @unpack deftest_1(self,txt1,txt2): print(txt1) print(txt2) if__name__=='__main__': unittest.main(verbosity=2)
ddt.txt文件中的内如下:
战狼,哪吒
流浪地球,复仇者联盟
运行结果:
函数readFile从文件中读取数据,unpack进行解包
========测试开始========
战狼
哪吒
========测试结束========
========测试开始========
流浪地球
复仇者联盟
========测试结束========
上面从文件中读取数据时先读取文件,再处理读取的结果,下面介绍一个直接操作文件的方法
从ddt中导入file_data,导入yaml,读取数据的文件类型必须为.yml类型的文件。
#coding=utf-8 importunittest fromddtimportddt fromddtimportdata fromddtimportunpack fromddtimportfile_data importyaml #类之前定义装饰器,表示在类中要使用ddt了 @ddt classMyTestCase(unittest.TestCase): defsetUp(self)->None: print('{:=^20}'.format("测试开始")) deftearDown(self)->None: print("{:=^20}".format('测试结束')) #直接的文件读取,直接操作一个文件 @file_data('ddt2.yml') deftest_5(self,txt): print(txt) if__name__=='__main__': unittest.main(verbosity=2)
ddt2.yml文件内容如下:
name:'skx' info:'hust'
运行结果:
========测试开始========
skx
========测试结束========
========测试开始========
hust
========测试结束========
2.2ddt数据驱动
打开浏览器进入百度查询的例子中我们发现除了输入的参数不同,test_1(self)和test_2(self)完全相同,这里我们就要通过data设定参数实现在一个测试用例中输入不同的参数
#coding=utf-8 importunittest fromseleniumimportwebdriver importtime fromddtimportddt fromddtimportdata #在class前定义@ddt,用于表示要使用ddt了 @ddt classforTestTest(unittest.TestCase): defsetUp(self): self.driver=webdriver.Chrome() self.driver.get('http://www.baidu.com') deftearDown(self): time.sleep(3) self.driver.quit() #data用于设定参数 @data('战狼','哪吒','流浪地球') deftest_1(self,txt): self.driver.find_element_by_id('kw').send_keys(txt) self.driver.find_element_by_id('su').click() if__name__=="__main__": unittest.main()
运行结果,谷歌浏览器被打开三次,进入百度,分别输入‘战狼',‘哪吒',‘流浪地球',每次浏览器关闭之后,才打开下一次,再进行搜索
上面的例子中,我们将输入的参数直接固定了,其实也可以通过文件读取,决定进入哪一个url和输入的参数
#coding=utf-8 importunittest fromseleniumimportwebdriver importtime fromddtimportddt fromddtimportdata fromddtimportunpack defreadFile(): params=[] file=open('forTest3.txt','r',encoding='gbk') forlineinfile.readlines(): params.append(line.strip('\n').split(',')) returnparams @ddt classforTestTest(unittest.TestCase): defsetUp(self): self.driver=webdriver.Chrome() deftearDown(self): time.sleep(3) self.driver.quit() #data用于设定参数,表示参数由readFile()函数的返回值决定 #unpack用于解析参数,例如将['http://www.baidu.com','战狼']分别赋值给url和txt @data(*readFile()) @unpack deftest_1(self,url,txt): self.driver.get(url) self.driver.find_element_by_id('kw').send_keys(txt) self.driver.find_element_by_id('su').click() if__name__=="__main__": unittest.main()
forTest3.txt文件中的内容如下:
http://www.baidu.com,战狼 http://www.baidu.com,哪吒
分析:
- readFile()函数打开文件,读取文件的每一行,按逗号‘,'划分关键字,
- unpack用于解析参数,ddt对于数据的解析方式为,解析一个,传参一个,所以函数中url和txt的参数顺序不能调换。
运行结果,谷歌浏览器被打开两次,进入百度,分别输入‘战狼',‘哪吒',每次浏览器关闭之后,才打开下一次,再进行搜索
file_data是ddt中用于读取yml文件的装饰器
3yml文件的使用
这个插入一个小插曲,上面提到了yml文件,这里就简单讲解一下yml文件怎么使用。
从yml文件中直接读取数据可以生成字典,列表等,yml文件由一定的格式,我们通过实例来说明,yml_test.py从a.yml文件中读取文件并打印出来。
yml_test.py
importyaml file=open('a.yml',encoding='utf-8') res=yaml.load(file,Loader=yaml.FullLoader) print(res)
a.yml文件中的内容如下所示,冒号代表字典,字典结构可以嵌套,也可以生成列表,具体格式参考下面的a.yml文件。
a.yml
name:'skx' age:18 data: a:1 b:2 c:3 d:4 list: -a -b -c -d
打印的结果如下所示,生成四个字典元素,第三个字典元素为嵌套字典结构,第四个字典对应的value为列表。
{'name':'skx','age':18,'data':{'a':1,'b':2,'c':3,'d':4},'list':['a','b','c','d']}
如果将a.yml文件中的数据改为如下结构,则生成一个纯列表,打印的结果如下所示。
a.yml
-a -b -c -d
['a','b','c','d']
有了yml文件,我们就可以将测试数据放到yml文件中,从文件中获取参数,传入测试函数,完成测试。还是通过例子来讲解,yml_test2.yml中是一个列表,每个列表元素是一个字典,字典中有两个元素,name和age,使用file_data直接可以将yml_test2.yml传入测试用例中。
read_yml2.py
#coding=utf-8 importunittest fromddtimportddt fromddtimportfile_data importyaml #类之前定义装饰器,表示在类中要使用ddt了 @ddt classMyTestCase(unittest.TestCase): defsetUp(self)->None: print('{:=^20}'.format("测试开始")) deftearDown(self)->None: print("{:=^20}".format('测试结束')) @file_data('read_yml2_data.yml') deftest_yam(self,**kwargs): #获取参数中key为name的value print(kwargs["name"]) #获取为text的value print(kwargs["age"]) if__name__=='__main__': unittest.main(verbosity=2)
read_yml2_data.yml
- name:'Tom' age:13 - name:'Carl' age:20 - name:'Edward' age:28
运行结果:
========测试开始========
Tom
13
========测试结束========
========测试开始========
Carl
20
========测试结束========
========测试开始========
Edward
28
========测试结束========
4UnitTest断言用法
在UnitTest中,TestCase已经提供有封装好的断言方法进行断言校验。
断言:用于校验实际结果与预期结果是否匹型,在断言的内容选择上,是有要求的。
断言强调的是对于整个测试流程的结果进行判断,所以断言的内容是极为核心的。
上面的代码
#coding=utf-8 importunittest fromddtimportddt fromddtimportfile_data importyaml #类之前定义装饰器,表示在类中要使用ddt了 @ddt classMyTestCase(unittest.TestCase): defsetUp(self)->None: print('{:=^20}'.format("测试开始")) deftearDown(self)->None: print("{:=^20}".format('测试结束')) @file_data('read_yml2_data.yml') deftest_yam(self,**kwargs): #获取参数中key为name的value name=kwargs['name'] print(name) #这里做断言,当断言不相等的时候返回msg self.assertEqual(name,'Tom',msg='NotEqual') #获取为text的value print(kwargs["age"]) if__name__=='__main__': unittest.main()
- name:'Tom' age:13 - name:'Carl' age:20 - name:'Edward' age:28
可以看到第一个例子执行正确,后面的例子,执行结果和预期不一致,返回NotEqual,左侧的日志可以看到第一个用例执行成功,后面两个例子执行失败。
unittest框架的TestCase类提供以下方法用于测试结果的判断
方法 | 检查 |
---|---|
assertEqual(a,b) | a==b |
assertNotEqual(a,b) | a!=b |
assertTrue(x) | bool(x)isTrue |
assertFalse(x) | Bool(x)isFalse |
assertIs(a,b) | aisb |
assertIsNot(a,b) | aisnotb |
assertIsNone(x) | xisNone |
assertIsNotNone(x) | xisnotNone |
assertIn(a,b) | ainb |
assertNotIn(a,b) | anotinb |
assertIsInstance(a,b) | isinstance(a,b) |
assertNotIsInstance(a,b) | notisinstance(a,b) |
5UnitTest.skip()用法
假设我们有很多测试用例,有些我们需要执行,有些我们不想执行,不想执行的测试用例如何才能不执行呢,这就需要用到skip。
Skip用法:
- 在Case中,对于不需要运行的用例或者特定条件下不执行的用例,可以应用skip()来实现有条件执行,或者绝对性跳过,用于对指定用例进行不执行操作
- skip通过装饰器进行使用
还是通过案例进行讲解,下面有6个测试用例,2-5测试用例被屏蔽了,使用的方法不同,
- @unittest.skip(“xxx”)是无条件跳过,xxx为跳过的理由
- unittest.skipIf(1<2,‘xxx'),条件为True时跳过
- @unittest.skipUnless(1>2,‘xxx'),条件为False时跳过,和skipIf更好相反
- @unittest.expectedFailure,如果用例执行失败,则不计入失败的case数中
直接看例子更加直观
skip_t.py
#coding=utf-8 importunittest classMyTestCase(unittest.TestCase): defsetUp(self)->None: pass deftearDown(self)->None: pass deftest_1(self): print('1') #无条件跳过该条用例 @unittest.skip("不想运行") deftest_2(self): print('2') #有条件跳过操作,条件为True跳过 @unittest.skipIf(1<2,'1<2为True,条件成立,跳过执行') deftest_3(self): print('3') #有条件执行跳过,条件为False跳过 @unittest.skipUnless(1>2,'Unless的理由,条件为False,跳过执行') deftest_4(self): print('4') #如果用例执行失败,则不计入失败的case数中 @unittest.expectedFailure deftest_5(self): print('5') self.assertEqual(4,3,msg='NotEqual') deftest_6(self): print('6') if__name__=="__main__": unittest.main()
执行结果如下,可以看到,test_2,test_3,test_4跳过,test_5执行失败,但是不计入case数中
5UnitTest测试套件及runner应用
测试套件Suite作用:
- 用于给测试用例进行排序
- 管理测试用例
通过例子讲解最容易理解,看一个最简单的例子,下面的代码中有五个测试用例,程序运行的结果和测试用例在代码中位置是没有关系的,结果永远打印12345,这是因为测试用例的执行顺序默认是按照字典顺序执行的,如何才能控制测试用例的执行顺序呢,这就需要使用测试套件了。
suite_case.py
importunittest classMyTestCase(unittest.TestCase): defsetUp(self)->None: pass deftearDown(self)->None: pass deftest_2(self): print("2") deftest_1(self): print("1") deftest_4(self): print("4") deftest_3(self): print("3") deftest_5(self): print("5") if__name__=="__main__": unittest.main()
运行结果:
1
2
3
4
5
再建一个py文件
#coding=utf-8 importunittest fromsuite_caseimport* #创建一个测试套件list suite=unittest.TestSuite() #方法一,添加测试用例(子元素)到测试套件(集合) suite.addTest(MyTestCase('test_3')) suite.addTest(MyTestCase("test_1")) suite.addTest(MyTestCase("test_5")) #套件通过TextTestRunner对象运行,功能相当于unittest.main() runner=unittest.TextTestRunner() runner.run(suite)
我们首先创建一个测试套件,然后向测试套件中添加测试用例,最后创建TextTestRunner对象,调用run函数运行测试用例。这样我们不仅可以控制测试用例的执行顺序,还可以控制运行哪个测试用例。
结果如下:
3
1
5
上面的方法每次添加测试用例都需要调用addTest函数,能不能一次添加多个测试用例呢,可以的,将测试用例写成一个列表,通过addTests函数可以一次添加多个测试用例
#coding=utf-8 importunittest fromsuite_caseimport* #创建一个测试套件list suite=unittest.TestSuite() #方法二,批量添加测试用例 cases=[MyTestCase('test_3'),MyTestCase('test_1'),MyTestCase('test_5')] suite.addTests(cases) #套件通过TextTestRunner对象运行,功能相当于unittest.main() runner=unittest.TextTestRunner() runner.run(suite)
如果测试用例非常多,或者有多个文件中的测试用例都需要测试,这样添加也不是很方便,我们好可以按照文件路径,将该路径下需要测试的文件添加进测试套件中
#coding=utf-8 importunittest fromsuite_caseimport* #创建一个测试套件list suite=unittest.TestSuite() #方法三,批量运行多个unittest类 test_dir='./' #start_dir参数指定文件路径,pattern执行规则,'s*.py'表示以"s"开头,".py"的都加入测试套件中 discover=unittest.defaultTestLoader.discover(start_dir=test_dir,pattern='s*.py') runner=unittest.TextTestRunner() runner.run(discover) #通过run函数运行测试用例
还可以执行类的名字,执行该类下面所有的测试用例,使用loadTestsFromName函数或者loadTestsFromTestCase都可以,案例如下:
#coding=utf-8 importunittest fromsuite_caseimport* #创建一个测试套件list suite=unittest.TestSuite() #方法四,给出文件名和类名,就能测试所有的测试用例 suite.addTests(unittest.TestLoader().loadTestsFromName('suite.MyTestCase')) #套件通过TextTestRunner对象运行,功能相当于unittest.main() runner=unittest.TextTestRunner() runner.run(suite)
#coding=utf-8 importunittest fromsuite_caseimport* #创建一个测试套件list suite=unittest.TestSuite() #方法五,给出类名,就能测试所有的测试用例 suite.addTests(unittest.TestLoader().loadTestsFromTestCase(MyTestCase)) #套件通过TextTestRunner对象运行,功能相当于unittest.main() runner=unittest.TextTestRunner() runner.run(suite)
6UnitTest+HTMLTestRunner自动化实现
通过HTMLTestRunner我们可以将测试结果生成html文件,通过网页端进行查看。步骤如下:
1.导入环境
下载HTMLTestRunner.py文件,下载地址
点进入HTMLTestRunner.py,右键另存为就可以下载到本地。
下载后,把HTMLTestRunner.py文件复制到Python安装路径下的lib文件夹中(我的安装路径是:C:\Users\Administrator\AppData\Local\Programs\Python\Python38\Lib)。在python3中用HTMLTestRunner.py报importError“:Nomodulenamed'StringIO'解决办法,原因是官网的是python2语法写的,看官手动把官网的HTMLTestRunner.py改成python3的语法。
修改内容:
- 第94行,将importStringIO修改成importio
- 第539行,将self.outputBuffer=StringIO.StringIO()修改成self.outputBuffer=io.StringIO()
- 第642行,将ifnotrmap.has_key(cls):修改成ifnotclsinrmap:
- 第631行,将print>>sys.stderr,‘\nTimeElapsed:%s'%(self.stopTime-self.startTime)修改成print(sys.stderr,‘\nTimeElapsed:%s'%(self.stopTime-self.startTime))
- 第766行,将uo=o.decode(‘latin-1')修改成uo=e
- 第772行,将ue=e.decode(‘latin-1')修改成ue=e
2.导包
fromHTMLTestRunnerimportHTMLTestRunner
下面就通过案例进行演示
suite_case.py文件
importunittest classMyTestCase(unittest.TestCase): defsetUp(self)->None: pass deftearDown(self)->None: pass deftest_2(self): print("2") deftest_1(self): print("1") deftest_4(self): print("4") deftest_3(self): print("3") deftest_5(self): print("5") if__name__=="__main__": unittest.main()
#coding=utf-8 importunittest fromsuite_caseimportMyTestCase fromHTMLTestRunnerimportHTMLTestRunner importos suite=unittest.TestSuite() report_path='./report/' report_file=report_path+'report.html' #路径不存在就创建一个文件夹 ifnotos.path.exists(report_path): os.mkdir(report_path) else: pass report_name='测试报告名称' report_title='测试报告标题' report_desc='测试报告描述' withopen(report_file,'wb')asreport: suite.addTests(unittest.TestLoader().loadTestsFromTestCase(MyTestCase)) #套件结合TextTestRunner对象进行运行,相当于unittest.mian() #如果结合HTMLTestRunner使用,则需要调用HTMLTestRunner中的运行器 runner=HTMLTestRunner(stream=report,title=report_title,description=report_desc) runner.run(suite)
运行就会成成report.html文件,浏览器打开该文件,如下所示:
这样就生成一个比较直观的测试报告
总结
到此这篇关于Python自动化之UnitTest框架实战的文章就介绍到这了,更多相关Python自动化UnitTest框架实战内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。