接口测试报告_纯html + Python代码
上次写了 接口测试报告的理论部分,剩下测试报告没写完,这篇就写测试报告!!!
jmeter 测试结果文件获取方式
很Easy的,在查看结果树是上添加一个文件名就好了。
保存完的结果是下图 ↓ 的样子
先给大家看个图片
HTML5 + Python3.7 代码
当中 包含了断言结果,请求数据,响应数据,还可以通过js实时发送接口请求。但是接口需要特殊认证就没办法了。
代码我就粘在这里了需要的自取。
# -*- coding:utf-8 -*-
import xml.dom.minidom
# jmeter 查看结果树生成的报告文件地址
xmlPath = "C:\\Users\\Administrator\\Desktop\\ZWCMS\\ZWCMS_res_tree.xml"
# 输出html 文件的文件地址
output_res_filepath = 'C:\\Users\\Administrator\\Desktop\\1.html'
# 主要的html
Html_template = """
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>接口测试报告</title>
<meta name="generator" content="HTMLTestRunner 0.8.2.2"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script src="http://libs.baidu.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<!-- 引入 echarts.js -->
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts/echarts-all-3.js"></script>
<script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts-stat/ecStat.min.js"></script>
<script type="text/javascript"
src="http://echarts.baidu.com/gallery/vendors/echarts/extension/dataTool.min.js"></script>
%(style)s
</head>
<body>
<!-- 汇总部分 -->
%(The_summary_section)s
<!-- 饼图部分 -->
%(The_pie_chart_part)s
<!-- 中间的一个空行 -->
<div><span> </span></div>
<!-- 详细报告部分 -->
<div class="col-md-12">
<div class="tab-content">
<table class="table table-bordered">
<thead class="interface_thead">
<td class="text-center">接口编号</td>
<td class="text-center">接口路径</td>
<td class="text-center">接口名称</td>
<td class="text-center">用例数量</td>
<td class="text-center">通过数量</td>
<td class="text-center">失败数量</td>
<td class="text-center">错误数量</td>
<td class="text-center">跳过数量</td>
</thead>
%(Detailed_report_section)s
</table>
</div>
</div>
</body>
<!-- 下面的js 是控制元素展开与隐藏的,还有实时发送post请求的js -->
<script type="application/javascript">
// 展开隐藏 当前ID 下的所有用例
show_conceal_case = function(id){
for(let i = 0 ; i < 999999 ; i++){
let full_id = id + "_"+ i;
let e = document.getElementById(full_id);
if (e){
if (e.className === "hiddenRow"){
e.className = ""
}else{
e.className = "hiddenRow";
conceal_case_details(full_id);
conceal_case_assertionResult(full_id);
conceal_send_case_details(full_id)
}
}else{
break;
}
}
};
// 隐藏用例详情,隐藏所有用例是使用此方法
conceal_case_details = function (id) {
let e = document.getElementById(id + "_details");
if (e) {
e.className = "hiddenRow";
}
};
// 隐藏 用例详情,断言,实时验证结果 // 收起按钮使用
conceal_all_case_details = function(id){
conceal_case_details(id);
conceal_case_assertionResult(id);
conceal_send_case_details(id);
conceal_conceal_all(id)
};
// 显示,隐藏用例详情
show_case_details = function (id) {
let e = document.getElementById(id + "_details");
if (e) {
if(e.className === "hiddenRow"){
show_conceal_all(id);
e.className = ""
}else{
e.className = "hiddenRow";
conceal_conceal_all(id);
}
}
};
// 隐藏用例详情
conceal_case_details = function (id) {
let e = document.getElementById(id + "_details");
if (e) {
e.className = "hiddenRow";
}
};
// 显示,隐藏断言详情
show_conceal_case_assertionResult = function (id) {
let es = document.getElementsByName(id + "_assertionResult");
for (let i = 0 ; i < es.length ; i ++) {
if (es[i].className === "hiddenRow"){
es[i].className = "";
show_conceal_all(id);
}else{
es[i].className = "hiddenRow" ;
conceal_conceal_all(id);
}
}
};
// 显示断言详情
show_case_assertionResult = function (id) {
let es = document.getElementsByName(id + "_assertionResult");
for (let i = 0 ; i < es.length ; i ++) {
es[i].className = "";
}
show_conceal_all(id)
};
// 隐藏断言详情
conceal_case_assertionResult = function (id) {
let es = document.getElementsByName(id + "_assertionResult");
for (let i = 0 ; i < es.length ; i ++) {
es[i].className = "hiddenRow";
}
conceal_conceal_all(id)
};
// 隐藏实时发送信息
conceal_send_case_details = function (id) {
let e = document.getElementById(id + "_send_data");
if (e) {
e.className = "hiddenRow";
}
};
// 显示收起按钮
show_conceal_all = function (id) {
let e = document.getElementById(id + "_conceal_all");
if (e) {
e.className = "";
}
};
// 隐藏收起按钮
conceal_conceal_all = function (id) {
let e = document.getElementById(id + "_conceal_all");
if (e) {
e.className = "hiddenRow";
}
};
// 发送接口数据
send_case_details = function (id) {
// 获取请求信息:
let datalocal = id+"_details";
let send_data_header = document.getElementById(id + "_send_data_header").innerHTML;
send_data_header = send_data_header.replace(/\s/g,"");
let send_data_headers = send_data_header.split("<br>");
let url = document.getElementById(id + "_send_data_path").innerHTML;
let request_body = document.getElementById(id + "_send_data_body").innerText;
// let request_method = document.getElementById(id + "_send_data_method").innerHTML;
let send_data_headers_res = "" ;
//第一步:创建需要的对象
let httpRequest = new XMLHttpRequest();
//第二步:打开连接/***发送json格式文件必须设置请求头 ;如下 - */
httpRequest.open('POST', url, true);
//设置请求头 注:post方式必须设置请求头(在建立连接后设置请求头)
for(let headers_index in send_data_headers){
let headers = send_data_headers[headers_index];
if(headers.indexOf("Content-Type:") !== -1){
send_data_headers_res += headers + "<br>" ;
let hs = headers.split(":");
httpRequest.setRequestHeader(hs[0],hs[1]);
}
}
// send 需要json 的字符串,而不是对象。
console.info(request_body);
httpRequest.send(request_body);
// httpRequest.send(JSON.stringify(obj));
/**
* 获取数据后的处理程序
*/
//请求后的回调接口,可将请求成功后要执行的程序写在其中
httpRequest.onreadystatechange = function () {
//获取到服务端返回的数据
let json = httpRequest.responseText;
let headers ;
let res ;
// js 会和服务器连接多次,这个函数也会调用多次,因此这个判断时确定是否是就绪状态。
if(httpRequest.readyState === 4 ) {
//验证请求是否发送成功
if (httpRequest.status === 200) {
headers = httpRequest.getAllResponseHeaders();
} else if (httpRequest.status === 404) {
json = "网站响应结果是 404 看看是不是路径写错了?";
} else if (httpRequest.status === 400) {
json = "网站响应结果是 400 看看是不是参数写错了?";
} else if(httpRequest.status === 415) {
json = "网站响应结果是 415 请求头写错了?";
}else if(httpRequest.status === 419) {
json = "网站响应结果是 419 跨域访问权限不足,这是服务器的问题。";
}else{
json = "网站响应结果是 " + httpRequest.status + "我也不知道哪错了";
}
document.getElementById(id + "_send_data_path_res").innerHTML = url;
document.getElementById(id + "_send_data_body_res").innerHTML = request_body;
document.getElementById(id + "_send_data_header_res").innerHTML = send_data_headers_res;
document.getElementById(id + "_response_data_header_res").innerHTML = `response_Code: ${httpRequest.status}<br>${headers}`;
document.getElementById(id + "_response_data_body_res").innerHTML = json;
let e = document.getElementById(id + "_send_data");
if (e) {
e.className = "";
}
show_conceal_all(id);
}
};
}
</script>
</html>
"""
# 页面的style样式
style = """
<style type="text/css" media="screen">
body {
margin: 0;
font-family: "宋体", sans-serif;
font-size: 18px ;
line-height: 1.5;
color: #333333;
}
.table {
margin-bottom: 1px
width: 100% ;
}
.hiddenRow {
display: none;
}
.container-fluid {
padding-right: 120px ;
padding-left: 120px ;
}
.nav-tabs li {
width: 186px ;
text-align: center ;
}
.interface_thead{
background-color: #307dd9;
font-weight:bold;
border-width: 5px ;
color: blanchedalmond;
}
.interface_pass{
background-color: #998eb8;
font-weight:bold;
}
.interface_failed{
background-color: #d99a4e;
font-weight:bold;
}
.interface_Skip{
background-color: #676767;
font-weight:bold;
}
.interface_error{
background-color: #df324d;
font-weight:bold;
}
.data_details{
text-align:left;
}
.summary_section{
width:100%;
height:10px;
}
</style>
"""
# 测试汇总信息
The_summary_section = """
<div class="col-md-4" style="Background-Color:#F5F5F5; height:auto">
<h3>测试基本信息</h3>
<table class="table table-hover table-bordered summary_section">
<tbody>
<tr class="info">
<td class="text-center">执行时间</td>
<td class="text-center">%(case_run_Start_time)s</td>
</tr>
<tr class="info">
<td class="text-center">测试用时</td>
<td class="text-center">%(case_run_time)s</td>
</tr>
<tr class="info">
<td class="text-center">接口数量</td>
<td class="text-center">%(all_inteface)s</td>
</tr>
<tr class="info">
<td class="text-center">总用例数</td>
<td class="text-center">%(all_case)s</td>
</tr>
<tr class="info">
<td class="text-center">执行用例数</td>
<td class="text-center">%(run_case)s</td>
</tr>
<tr class="info">
<td class="text-center">失败用例数</td>
<td class="text-center">%(all_failed)s</td>
</tr>
<tr class="info">
<td class="text-center">跳过用例数</td>
<td class="text-center">%(all_Skip)s</td>
</tr>
</tbody>
</table>
</div>
"""
# 生成图片报告信息
The_pie_chart_part = """
<div class="col-md-8" style="Background-Color:#F5F5F5; height:auto" >
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="height:300px ;"></div>
<script type="text/javascript">
var myChart = echarts.init(document.getElementById('main'));
var option = {
backgroundColor: '#F5F5F5', //背景色
title: {
text: '测试统计数据',
x: 'center'
},
legend: {
orient: 'vertical',
x: 'left',
data: ['成功', '失败', '跳过']
},
color: ['#998eb8', '#d99a4e', '#676767'],
calculable: true,
series: [{
name: '测试结果',
type: 'pie',
radius: '55%(per)s',
center: ['50%(per)s', '60%(per)s'],
startAngle: 135,
data: [{
value: %(all_Pass)s,
name: '成功',
itemStyle: {
normal: {
label: {
formatter: '{b} : {c} ({d}%(per)s)',
textStyle: {
align: 'left',
fontSize: 15,
}
},
labelLine: {
length: 40,
}
}
}
}, {
value: %(all_failed)s,
name: '失败',
itemStyle: {
normal: {
label: {
formatter: '{b} : {c} ({d}%(per)s)',
textStyle: {
align: 'right',
fontSize: 15,
}
},
labelLine: {
length: 40,
}
}
}
}, {
value: %(all_Skip)s,
name: '未检验',
itemStyle: {
normal: {
label: {
formatter: '{b} : {c} ({d}%(per)s)',
textStyle: {
align: 'right',
fontSize: 15,
}
},
labelLine: {
length: 40,
}
}
}
}],
}]
};
// 为echarts对象加载数据
myChart.setOption(option);
</script>
</div>
"""
# 测试接口列表
Detailed_report_section = """
<tbody class="">
%(case_pandect)s
<!-- 接口用例详情 -->
%(case_pandect_hider)s
%(case_details)s
</tbody>
"""
case_pandect_hider = """
<tr id="%(sid)s_%(id)s" name="interface_case_details_header" class="hiddenRow">
<td class="text-center interface_thead">编号</td>
<td class="text-center interface_thead">用例名称</td>
<td class="text-center interface_thead">执行时间</td>
<td class="text-center interface_thead">执行结果</td>
<td class="text-center interface_thead">请求方式</td>
<td class="text-center interface_thead">断言数量</td>
<td colspan="2" class="text-center interface_thead">操作</td>
<td class="text-center interface_thead" style="background-color: white"></td>
</tr>"""
# 测试用例列表
case_pandect = """
<tr οnclick="show_conceal_case('%(id)s')" class="interface_pass">
<td class="text-center" >%(id)s</td>
<td class="text-center">%(url_key)s</td>
<td class="text-center">%(interface_name)s</td>
<td class="text-center">%(count)s</td>
<td class="text-center">%(Pass)s</td>
<td class="text-center %(failed_style)s">%(failed)s</td>
<td class="text-center %(error_style)s">%(error)s</td>
<td class="text-center %(Skip_style)s">%(Skip)s</td>
</tr>
"""
# 测试用例详情
case_details = """
<tr id="%(sid)s_%(id)s" name="interface_case_details" class="hiddenRow">
<td class="text-center %(case_run_status)s">%(id)s</td>
<td class="text-center %(case_run_status)s">%(lb)s</td>
<td class="text-center %(case_run_status)s">%(t)s</td>
<td class="text-center %(case_run_status)s">%(rc)s</td>
<td class="text-center %(case_run_status)s">%(method)s</td>
<td class="text-center %(case_run_status)s"
οnclick="show_conceal_case_assertionResult('%(sid)s_%(id)s')">%(arrss)s</td>
<td class="text-center %(case_run_status)s" οnclick="show_case_details('%(sid)s_%(id)s')">查看详情</td>
<td class="text-center %(case_run_status)s" οnclick="send_case_details('%(sid)s_%(id)s')">实时发送数据</td>
<td class="text-center %(case_run_status)s" style="background-color: white"></td>
</tr>
<!-- 查看用例详情 -->
<tr id="%(sid)s_%(id)s_details" name="interface_case_details" class="hiddenRow">
<!-- <td class="text-center" style="background-color: white"></td>-->
<td colspan="2" class="text-center" style="width: 300px">
<div name = "send_data" class="data_details" >
<div name = "send_data_URL">
<h2>请求地址:<br></h2>
<div id="%(sid)s_%(id)s_send_data_path" >%(URL_path)s</div>
</div>
<div name = "send_data_header">
<h2>请求头:<br></h2>
<div id="%(sid)s_%(id)s_send_data_header" >%(requestHeader)s</div>
</div>
<div name = "send_data_body">
<h2>请求数据:<br></h2>
<div id="%(sid)s_%(id)s_send_data_body" >%(queryString)s</div>
</div>
</div>
</td>
<td colspan="6" class="text-center" style="width: 300px">
<div name = "request_data" class="data_details" >
<div name = "response_data_header">
<h2>响应头:<br></h2>
%(responseHeader)s
</div>
<div name = "response_data_body">
<h2>响应数据:<br></h2>
%(responseData)s
</div>
</div>
</td>
</tr>
<tr id="%(sid)s_%(id)s_send_data" name="interface_case_send_data" class="hiddenRow">
<td colspan="2" class="text-center" style="width: 300px">
<div name = "send_data_res" class="data_details" >
<div name = "send_data_URL_res">
<h2>请求地址:<br></h2>
<div id="%(sid)s_%(id)s_send_data_path_res" ></div>
</div>
<div name = "send_data_header_res">
<h2>请求头:<br></h2>
<div id="%(sid)s_%(id)s_send_data_header_res" >
</div>
</div>
<div name = "send_data_body_res">
<h2>请求数据:<br></h2>
<div id="%(sid)s_%(id)s_send_data_body_res" ></div>
</div>
</div>
</td>
<td colspan="6" class="text-center" style="width: 300px">
<div name = "request_data" class="data_details" >
<div name = "response_data_header">
<h2>响应头:<br></h2>
<div id = "%(sid)s_%(id)s_response_data_header_res"></div>
</div>
<div name = "response_data_body">
<h2>响应数据:<br></h2>
<div id="%(sid)s_%(id)s_response_data_body_res" ></div>
</div>
</div>
</td>
</tr>
%(interface_case_assertionResult)s
<tr id="%(sid)s_%(id)s_conceal_all" class="hiddenRow">
<td colspan="8" class="interface_thead" οnclick="conceal_all_case_details('%(sid)s_%(id)s')">
<center>收起↑详情</center>
</td>
</tr>
"""
# 用例断言信息
interface_case_assertionResult = """
<tr name="%(sid)s_%(id)s_assertionResult" class="hiddenRow">
<td class="%(case_run_status)s">name: %(name)s</td>
<td class="%(case_run_status)s">failure: %(failure)s</td>
<td class="%(case_run_status)s">error: %(error)s</td>
<td colspan="5" class="%(case_run_status)s">failureMessage: %(failureMessage)s</td>
</tr>
"""
# 这是一个接口的信息,里面包括关于这个接口的所有case,与case详情
Detailed_report_section_list = """"""
# 这是一个用例的信息,里面包括每个case的信息。
case_details_list = """"""
# --------------------上面是准备 html 报告的数据
# --------------------下面是读取jmeter xml报告数据
dom = xml.dom.minidom.parse(xmlPath)
root_tag_neme = "httpSample"
root = dom.documentElement
root_tags = dom.getElementsByTagName(root_tag_neme)
# 先把结果去到放到dict中
result_dict = {}
result_list = []
case_run_Start_time = root_tags[0].getAttribute("ts")
case_run_End_time = root_tags[len(root_tags) - 1].getAttribute("ts")
case_run_time = str(int(case_run_End_time) - int(case_run_Start_time))
for root_tag in root_tags:
# 请求地址:
URL_path = root_tag.getElementsByTagName("java.net.URL")[0].firstChild.data
URL_path_ = URL_path.replace("http://", "")
case_key = URL_path_[URL_path_.find("/"):]
# 没有请求数据的接口需要特殊处理
firstChild = root_tag.getElementsByTagName("queryString")[0].firstChild
queryString = firstChild is not None and firstChild.data.replace("\n", "<br>") or "{}"
# 获取断言列表
assertionResults = root_tag.getElementsByTagName("assertionResult")
assertionResult_R = []
for assertionResult in assertionResults:
name = assertionResult.getElementsByTagName("name")[0].firstChild.data,
failure = assertionResult.getElementsByTagName("failure")[0].firstChild.data,
error = assertionResult.getElementsByTagName("error")[0].firstChild.data,
failureMessage = ""
if failure[0] == 'true' or error[0] == "true":
failureMessage = assertionResult.getElementsByTagName("failureMessage")[0].firstChild.data
assertionResult_R.append(dict(
name=name[0],
failure=failure[0],
error=error[0],
failureMessage=failureMessage, ))
res = dict(
URL_path=URL_path,
# 名称
lb=root_tag.getAttribute("lb"),
# 响应代码
rc=root_tag.getAttribute("rc"),
# 响应信息
rm=root_tag.getAttribute("rm"),
# 执行时间
t=root_tag.getAttribute("t"),
# 请求头:
requestHeader=root_tag.getElementsByTagName("requestHeader")[0].firstChild.data.replace("\n", "<br>"),
# 请求数据:
queryString=queryString,
# 请求方法
method=root_tag.getElementsByTagName("method")[0].firstChild.data.replace("\n", "<br>"),
# 响应数据:
responseData=root_tag.getElementsByTagName("responseData")[0].firstChild.data.replace("\n", "<br>"),
# 响应头:
responseHeader=root_tag.getElementsByTagName("responseHeader")[0].firstChild.data.replace("\n", "<br>"),
assertionResult_R=assertionResult_R,
)
if case_key in result_dict.keys():
result_dict[case_key].append(res)
else:
result_dict[case_key] = [res]
# 数据已经在集合里面了,现在开始分析。
sid = 0
case_pandect_hider_ = """"""
all_Pass = 0
all_error = 0
all_Skip = 0
all_failed = 0
# 遍历所有接口
for url_key in result_dict.keys():
sid += 1
res_data = result_dict[url_key]
Pass = 0
error = 0
Skip = 0
failed = 0
# 获取名称
interface_name = res_data[0]['lb'].replace("P0_", "")
for id_id in range(0, len(res_data)):
case_run_status = ''
case_details_dict = res_data[id_id]
# 计算不同状态的用例数量
rc = case_details_dict['rc'][:1]
if rc == "2" or rc == "3":
Pass += 1
elif rc == "4":
error += 1
case_run_status = "interface_error"
elif rc == "5":
failed += 1
case_run_status = "interface_failed"
pass
interface_case_assertionResult_list = ""
is_fail = "false"
assertion_status = ""
for assertionResult in case_details_dict["assertionResult_R"]:
assertion_status = assertionResult["failure"] == "true" and "interface_failed" or \
assertionResult["error"] == "true" and "interface_error" or ""
if is_fail == "false":
if assertionResult["failure"] == "true":
Pass -= 1
failed += 1
case_run_status = "interface_failed"
is_fail = "true"
if assertionResult["error"] == "true":
error += 1
Pass -= 1
case_run_status = "interface_error"
is_fail = "true"
interface_case_assertionResult_list += interface_case_assertionResult % dict(
assertionResult,
sid=sid, id=id_id,
case_run_status=assertion_status,
)
case_pandect_hider_ = case_pandect_hider % dict(sid=sid, id=len(res_data))
case_details_dict = dict(case_details_dict, sid=sid, id=id_id,
arrss=len(case_details_dict["assertionResult_R"]),
case_run_status=case_run_status)
case_details_list = case_details_list + case_details % dict(
case_details_dict,
interface_case_assertionResult=interface_case_assertionResult_list)
all_Pass += Pass
all_error += error
all_Skip += Skip
all_failed += failed
case_pandect_ = case_pandect % dict(
id=sid,
url_key=url_key,
interface_name=interface_name,
count=Pass + error + Skip + failed,
Pass=Pass,
error=error,
Skip=Skip,
failed=failed,
failed_style=(failed == 0 and "interface_pass" or "interface_failed"),
Skip_style=(Skip == 0 and "interface_pass" or "interface_Skip"),
error_style=(error == 0 and "interface_pass" or "interface_error")
)
Detailed_report_section_list = Detailed_report_section_list + Detailed_report_section % dict(
case_pandect=case_pandect_,
case_pandect_hider=case_pandect_hider_,
case_details=case_details_list)
case_details_list = """"""
summary_section = The_summary_section % dict(
case_run_Start_time=case_run_Start_time,
case_run_time=case_run_time,
all_inteface=str(len(result_dict.keys())),
all_case=str(all_Pass + all_error + all_Skip + all_failed),
run_case=str(all_Pass + all_error + all_failed),
all_failed=str(all_error + all_failed),
all_Skip=str(all_Skip)
)
pie_chart_part = The_pie_chart_part % dict(
all_Pass=str(all_Pass),
all_Skip=str(all_Skip),
all_failed=str(all_error + all_failed),
per="%",
)
a = Html_template % dict(
The_summary_section=summary_section,
The_pie_chart_part=pie_chart_part,
Detailed_report_section=Detailed_report_section_list,
style=style
)
fp = open(output_res_filepath, 'wb')
fp.write(bytes(a.encode("utf-8")))
fp.close()
还有很多需要优化的地方,有建议给我评论!!!