HTMLTestRunner_Chart 基于unittest的测试报告,使用详情见demo
参考链接:
http://tungwaiyip.info/software/HTMLTestRunner.html
https://github.com/GoverSky/HTMLTestRunner_cn
优化报告内容
- 测试报告中文显示,优化一些断言失败正文乱码问题
- 新增错误和失败截图,展示到html报告里
- 增加饼图统计
- 失败后重试功能
- 保存近10次测试结果,并通过柱状图展示
- 切换测试日期,展示历史测试结果
兼容python2.x 和3.x
注意:
- 在是python3.x 中,如果在这里setUp里初始化driver ,因为3.x版本 unittest 运行机制不同,会导致用力失败时截图失败,目前只有采用捕获异常来截图,或者在setUpClass里初始化driver
- driver初始化变量名必须命名为driver
报告首页:
用例截图:
失败饼图:
历史走势:
微信打赏:
失败重试:
- 生成报告的参数里面加了一个参数retry=1,这个表示用例失败后,会重新跑一次。
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(case_01)
runner = HTMLTestRunner(
title="带截图,饼图,折线图,历史结果查看的测试报告",
description="",
stream=open("./demo.html", "wb"),
verbosity=2,
retry=0,
save_last_try=True)
runner.run(suite)
保存测试结果到json文件:
def mkdir_json(self):
is_exists = os.path.exists(self.path)
# 判断结果
if not is_exists:
try:
# 如果不存在则创建目录
# 创建目录操作函数
with open(self.path, "w+") as f:
f.write("var data = []")
return True
except Exception as e:
print(e)
return False
else:
return True
def Write(self, title, heading, desc, data):
try:
with open(self.path, "r+") as f:
all_data = f.read().split(" = ", 1)
data_json = all_data[1]
data_json = eval(data_json)
if len(data_json) >= 10:
del data_json[0]
description = dict()
description["startTime"] = heading[0][1]
description["duration"] = heading[1][1]
if PY3K:
description["title"] = title
description["status"] = heading[2][1]
description["desc"] = desc
description["data"] = data
status = heading[2][1].split(" ")
for j in range(0, len(status)):
if status[j] == "通过":
description["success"] = str(status[j + 1])
if status[j] == "失败":
description["fail"] = str(status[j + 1])
if status[j] == "错误":
description["error"] = str(status[j + 1])
else:
description["title"] = title.encode("gbk")
description["status"] = heading[2][1].encode("gbk")
description["desc"] = desc.encode("gbk")
description["data"] = data.encode("gbk")
status = heading[2][1].split(" ")
for j in range(0, len(status)):
if status[j] == u"通过":
description["success"] = str(status[j + 1])
if status[j] == u"失败":
description["fail"] = str(status[j + 1])
if status[j] == u"错误":
description["error"] = str(status[j + 1])
data_json.append(description)
data_json = str(data_json)
f.seek(0)
f.truncate()
f.write(str("var data = " + data_json))
except IndexError:
sys.stderr.write("JSON初始化内容有误! 初始化内容’var data = []‘")
错误/失败截图,修改addError和addFail函数:
def addFailure(self, test, err):
self.failure_count += 1
self.status = 1
TestResult.addFailure(self, test, err)
_, _exc_str = self.failures[-1]
output = self.complete_output()
self.result.append((1, test, output, _exc_str))
if not getattr(test, "driver",""):
pass
else:
try:
driver = getattr(test, "driver")
test.imgs.append(driver.get_screenshot_as_base64())
except Exception as e:
pass
if self.verbosity > 1:
sys.stderr.write('F ')
sys.stderr.write(str(test))
sys.stderr.write('\n')
else:
sys.stderr.write('F')
错误重试,修改stopTest函数:
def stopTest(self, test):
# Usually one of addSuccess, addError or addFailure would have been called.
# But there are some path in unittest that would bypass this.
# We must disconnect stdout in stopTest(), which is guaranteed to be called.
if self.retry:
if self.status == 1:
self.trys += 1
if self.trys <= self.retry:
if self.save_last_try:
t = self.result.pop(-1)
if t[0]==1:
self.failure_count-=1
else:
self.error_count -= 1
test=copy.copy(test)
sys.stderr.write("Retesting... ")
sys.stderr.write(str(test))
sys.stderr.write('..%d \n' % self.trys)
doc = test._testMethodDoc or ''
if doc.find('_retry')!=-1:
doc = doc[:doc.find('_retry')]
desc ="%s_retry:%d" %(doc, self.trys)
if not PY3K:
if isinstance(desc, str):
desc = desc.decode("utf-8")
test._testMethodDoc = desc
test(self)
else:
self.status = 0
self.trys = 0
self.complete_output()
HTML模板导入JSON历史结果,如果JSON出现错误,则历史结果和走势图错误:
<head>
<title>%(title)s</title>
<meta name="generator" content="%(generator)s"/>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<script type="text/javascript" src="%(jsonpath)s" charset="gbk"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/echarts/3.8.5/echarts.common.min.js"></script>
<!-- <script type="text/javascript" src="js/echarts.common.min.js"></script> -->
%(stylesheet)s
</head>