导语
之前已经基本完成了驱动程序调用用例脚本的执行,在探索框架(二)中也总结了如何利用HTMLTestRunner生成测试报告,本次尝试自定义编写测试报告,输出内容较为灵活。
一、修改框架UI原型设计
输出的测试报告可以选用CSV格式,也可以利用HTMLTestRunner生成测试报告,这里主要是要实践一下wxpython中如何使用单选按钮,修改原型图如下:
默认选中“生成CSV形式的测试报告”,这里只能单选其一,且不能取消单选,可以用wx.RadioButton,不得不说,wxpython还是比较好用的,非常方便就可以画好以上的原型图。根据之前探索框架(四) 的分析,这里需要稍作修改,画界面时,将两个单选按钮设计为一组(第二组),利用垂直分布boxsizer即可,将原先的下方的三个按钮设计为第三组,这样要注意在最后加入整体boxsizer中的顺序,具体代码如下:
# 测试框架界面类的实现
class testframe_UI():
# 初始化方法定义界面窗体控件及容器
def __init__(self):
......
# 定义选择测试报告类型的单选按钮
self.csv_radiobut = wx.RadioButton(self.panel, label="生成csv形式的测试报告")
# 默认选中csv_radiobut
self.csv_radiobut.SetValue(1)
self.html_radiobut = wx.RadioButton(self.panel, label="生成html形式的测试报告(仅支持unittest测试用例脚本)")
# 定义控件布局
def lauout_UI(self):
......
# 定义垂直放置的boxsizer2,用于存放两个单选按钮
boxsizer2 = wx.BoxSizer(wx.VERTICAL)
boxsizer2.Add(self.csv_radiobut, flag=wx.DOWN, border=10)
boxsizer2.Add(self.html_radiobut)
# 定义水平放置的boxsizer3,用于存放运行、重置、退出按钮(无改动,省略显示)
boxsizer3 = wx.BoxSizer()
......
# 定义垂直放置的boxsizer4,用于存放boxsizer1、boxsizer2、boxsizer3
boxsizer4 = wx.BoxSizer(wx.VERTICAL)
boxsizer4.Add(boxsizer1, flag=wx.TOP | wx.EXPAND, border=30)
boxsizer4.Add(boxsizer2, flag=wx.LEFT, border=40)
boxsizer4.Add(boxsizer3, flag=wx.LEFT, border=20)
# 设置boxsizer生效
self.panel.SetSizer(boxsizer4)
其中“......”省略了之前不需要修改的代码,这样一来,UI就画好了,接下来是与按钮相关的测试报告的生成,需要新设计一个测试报告类,初步设计如下:
二、生成测试报告
1、修改run_driver方法
因为加入了测试报告的选择,那么在点击“运行”按钮时需要去根据选项来执行相应的方法,所以在“运行”按钮绑定事件的方法run_driver需要做一个判断,修改代码如下:
def run_driver(self, event):
# 进行文本框的非空校验(代码不需要修改,在这里省略)
......
# 调用测试驱动程序
# 实例化测试驱动类
objdriver = testframe_driver()
# 根据单选按钮的选项,调用测试驱动类中的不同的执行方法
if self.csv_radiobut.GetValue() == 1:
objdriver.run_configfile_csv(self.configfile)
elif self.html_radiobut.GetValue() == 1:
objdriver.run_configfile_html(self.configfile)
2、新增CSV报告类和创建报告方法
根据run_driver方法的修改,而去调用的run_configfile_csv方法(原来为run_configfile)也需要修改,修改如下:
# 测试驱动类
class testframe_driver():
# 定义执行配置文件生成csv报告方法
def run_configfile_csv(self, configfile):
# 实例化测试报告类
objreport = testframe_report()
# 调用创建并打开文件方法
objreport.open_report()
# 读取配置文件内容(后续暂时不修改,省略显示)
......
在已经选好了生成CSV报告后,在读取配置文件运行脚本前,就需要创建打开测试报告,调用open_report方法(新建的类及方法),具体代码如下:
# 测试报告类
class testframe_report():
def __init__(self):
# 获取当前路径
self.curpath = os.getcwd()
# 获取项目路径
self.projectpath = os.path.abspath(os.path.dirname(self.curpath) + os.path.extsep + ".")
# 创建并打开文件
def open_report(self):
# 先判断报告是否存在,若存在就移除
reportpath = self.projectpath + "\\test_report\\fram_report\\framereport.csv"
if os.path.exists(reportpath):
os.remove(reportpath)
self.reportfile = open(reportpath, "a", newline="")
self.write = csv.writer(self.reportfile)
创建好了测试报告,接下来就是测试报告标题的设计部分。
3、在已创建的测试报告中写入标题行
标题一部分可以借用配置文件的标题,配置文件如下图所示
再在“是否执行”后加上一列“测试结论”,这样每个脚本中用例执行的结论也都可以清晰的展示了,这需要在测试报告类testframe_report()中加入新的方法写入标题write_title,具体代码如下:
# 测试报告类
class testframe_report():
def __init__(self):
# 创建并打开文件
def open_report(self):
# 写入报告标题
def write_title(self):
file = open(self.projectpath + "\config\config2.csv", "r")
table = csv.reader(file)
title = list(table)[0]
self.write.writerow(title + ["测试结论"])
self.reportfile.close()
仍然是在原先的run_configfile_csv方法中,读取完配置文件就可以去调用write_title写入标题,具体代码如下:
# 测试驱动类
class testframe_driver():
# 定义执行配置文件生成csv报告方法
def run_configfile_csv(self, configfile):
# 实例化测试报告类
objreport = testframe_report()
# 调用创建并打开文件方法
objreport.open_report()
# 读取配置文件内容
file = open(configfile, "r")
table = csv.reader(file)
# 调用写入报告标题方法
objreport.write_title()
# 跳过第一行
next(table)
for ele in table:
casename = ele[1]
stros = ('python ' + casename)
......
到这里,其实就可以先运行调试一下,看是否如预期一样生成了测试报告,并写入了标题,运行无问题以后,接下来就开始写入相关用例执行情况。
4、在写好的标题行下面逐行写入每个脚本的测试结论
这里先分析一下报告内容的组成,前面三列是可以从配置文件中去获取的,但是第四列“测试结论”,需要从不同的运行的脚本中去获取,且每个脚本中可能有多条用例,每条用例都会有相应的测试结论,最后需要把这一个脚本中所有的用例结论合在一起写入“测试结论”这一列,这样可以中间用一个临时文件过渡一下,将脚本运行完的所有用例的测试结论写入临时文件中,再去读取临时文件将整个内容写入测试报告的“测试结论”中。
需要在testframe_report()中先加入两个新方法,代码如下:
# 测试报告类
class testframe_report():
def __init__(self):
# 创建并打开文件
def open_report(self):
# 写入报告标题
def write_title(self):
# 将脚本执行的测试结果写入临时文件中
def write_tmp(self, scriptresult):
tmp = open(self.projectpath + "\\test_report\\fram_report\\tmp.txt", "w")
tmp.write(scriptresult+"\n")
tmp.close()
# 读取临时文件
def read_tmp(self):
tmp = open(self.projectpath + "\\test_report\\fram_report\\tmp.txt", "r")
result = tmp.read().split("\n")
self.result = result[0]
tmp.close()
这里需要注意两点,一是在每个脚本中需要去调用这个write_tmp方法,才可以将结论写入临时文件中,代码如下:
# 脚本程序文件内容省略,以下仅显示调用write_tmp方法
if __name__ == '__main__':
......
# 实例化框架脚本中的编写测试报告类
objreport=testframe_report()
# 调用测试报告类中的写入临时文件方法,将测试结果写入临时文件中
objreport.write_tmp(str(scriptresult))
二是写入临时文件的方式需要用“w”方式,因为每个脚本执行结束都会有新的结论,需要覆盖写入才行。此后就需要在原先的run_configfile_csv方法中,在调用运行完脚本以后,调用read_tmp方法,读取相关结论,代码如下:
# 测试驱动类
class testframe_driver():
# 定义执行配置文件生成csv报告方法
def run_configfile_csv(self, configfile):
......
# 跳过第一行
next(table)
for ele in table:
casename = ele[1]
stros = ('python ' + casename)
if ele[2] == "yes":
# 执行脚本
os.system(stros)
# 调用读取临时文件方法,获取测试用例执行结果
objreport.read_tmp()
# 调用写入测试结果方法
objreport.write_result(ele[0], ele[1], ele[2], objreport.result)
读取完一次临时文件中的内容,就需要调用一次write_result方法,将测试结论写入测试报告中,这样需要在testframe_report()中再加入一个方法write_result,代码如下:
# 测试报告类
class testframe_report():
def __init__(self):
# 创建并打开文件
def open_report(self):
# 写入报告标题
def write_title(self):
# 写入报告测试结论
def write_result(self, casenum, casename, runstate, result):
self.reportfile = open(self.projectpath + "\\test_report\\fram_report\\framereport.csv", "a", newline="")
self.write = csv.writer(self.reportfile)
self.write.writerow([casenum]+[casename]+[runstate]+[result])
self.reportfile.close()
# 将脚本执行的测试结果写入临时文件中
def write_tmp(self, scriptresult):
# 读取临时文件
def read_tmp(self):
到这里需要运行调试一下,看是否如预期将相应的测试结论写入测试报告中,大概报告如下:
5、在csv测试报告中最后写入统计数据
同样也是需要通过临时文件来过渡,有了之前写好的方法,只需要加上新的参数即可,首先是每个脚本中需要增加新的参数,成功总数和失败总数,代码如下:
# 脚本程序文件内容省略,以下仅显示调用write_tmp方法
if __name__ == '__main__':
......
# 实例化框架脚本中的编写测试报告类
objreport=testframe_report()
# 调用测试报告类中的写入临时文件方法,将测试结果、成功总数、失败总数写入临时文件中
objreport.write_tmp(str(scriptresult), str(obj.sucnum), str(obj.errnum))
其次是修改write_tmp和read_tmp方法,代码如下:
# 测试报告类
class testframe_report():
def __init__(self):
# 创建并打开文件
def open_report(self):
# 写入报告标题
def write_title(self):
# 写入报告测试结论
def write_result(self, casenum, casename, runstate, result):
# 将脚本执行的测试结果写入临时文件中
def write_tmp(self, scriptresult, sucnum, errnum):
tmp = open(self.projectpath + "\\test_report\\fram_report\\tmp.txt", "w")
tmp.write(scriptresult+"\n")
tmp.write(sucnum+"\n")
tmp.write(errnum+"\n")
tmp.close()
# 读取临时文件
def read_tmp(self):
tmp = open(self.projectpath + "\\test_report\\fram_report\\tmp.txt", "r")
result = tmp.read().split("\n")
self.result = result[0]
self.sucnum = result[1]
self.errnum = result[2]
tmp.close()
然后去修改run_configfile_csv方法中关于成功、失败、总耗时的统计,代码如下:
# 测试驱动类
class testframe_driver():
# 定义执行配置文件生成csv报告方法
def run_configfile_csv(self, configfile):
......
# 调用写入报告标题方法
objreport.write_title()
sucnum = 0
errnum = 0
starttime = datetime.datetime.now()
# 跳过第一行
next(table)
for ele in table:
casename = ele[1]
stros = ('python ' + casename)
if ele[2] == "yes":
# 执行脚本
os.system(stros)
# 调用读取临时文件方法,获取测试用例执行结果
objreport.read_tmp()
# 调用写入测试结果方法
objreport.write_result(ele[0], ele[1], ele[2], objreport.result)
sucnum = sucnum + int(objreport.sucnum)
errnum = errnum + int(objreport.errnum)
endtime = datetime.datetime.now()
totaltime = (endtime-starttime).seconds
# 调用写入统计方法
objreport.write_count(sucnum, errnum, totaltime)
最后在测试报告类中加入最后的统计方法write_count,代码如下:
# 测试报告类
class testframe_report():
def __init__(self):
# 创建并打开文件
def open_report(self):
# 写入报告标题
def write_title(self):
# 写入报告测试结论
def write_result(self, casenum, casename, runstate, result):
# 写入测试统计结果
def write_count(self, sucnum, errnum, totaltime):
self.reportfile = open(self.projectpath + "\\test_report\\fram_report\\framereport.csv", "a", newline="")
self.write = csv.writer(self.reportfile)
totalnum = sucnum + errnum
self.write.writerow(["测试用例总数"] + [totalnum] + ["条"])
self.write.writerow(["测试用例成功数"] + [sucnum] + ["条"])
self.write.writerow(["测试用例失败数"] + [errnum] + ["条"])
self.write.writerow(["执行用例总耗时"] + [totaltime] + ["秒"])
self.reportfile.close()
# 将脚本执行的测试结果写入临时文件中
def write_tmp(self, scriptresult, sucnum, errnum):
# 读取临时文件
def read_tmp(self):
至此,自定义的测试报告设计就完成了,最终的效果大概如下图(图5),其中的内容会根据配置文件的变化而相应的变化。
6、利用HTMLTestRunner生成测试报告
最后一项任务,是当选择了"生成html形式的测试报告(仅支持unittest测试用例脚本)"时,需要去调用相应的方法,这里需要说明的是,只有脚本是基于unittest编写的才会生效。由于是利用HTMLTestRunner生成测试报告,路径名和脚本名需要分开使用,可以设计新的配置文件,如下:
在测试驱动类testframe_driver中加入新的方法run_configfile_html,代码如下:
# 测试驱动类
class testframe_driver():
# 定义执行配置文件生成csv报告方法
def run_configfile_csv(self, configfile):
# 定义执行配置文件生成html报告方法
def run_configfile_html(self, configfile):
file = open(configfile, "r")
table = csv.reader(file)
next(table)
i = 1
for content in table:
if content[2] == "yes":
discover = unittest.defaultTestLoader.discover(content[0], pattern=content[1], top_level_dir="父路径")
htmlreport = open("测试报告路径\htmlreport" + str(i) + ".html", "wb")
working = HTMLTestRunner(stream=htmlreport, title="html测试报告", description="这是一个简单的描述信息")
working.run(discover)
i = i + 1
file.close()
由于是执行一个脚本输出一个报告,所以会存在多个报告,在报告名称中就加入了变量i。
至此,整个关于在框架驱动脚本中加入测试报告的内容就基本结束了,后续可以根据情况再做优化调整。
三、总结
本次学习如何使用wxpython中的单选按钮,并在原先的框架驱动程序中加入了生成测试报告这一个环节,可以根据需要去选择测试报告的格式,最后再做一个整个程序的梳理,如下图: