前言:本篇教程是基于Windows操作系统下搭建jmeter+ant+jenkins
基于linux请注意广深小龙更新!!!我们直接看下优化好的报告图:
一、环境准备
1、jdk1.8以上:
jdk安装教程详细参考:
①win:https://www.cnblogs.com/gsxl/p/11674981.html
②linux:https://www.cnblogs.com/gsxl/p/12128768.html
2、jmeter:
所有版本:https://archive.apache.org/dist/jmeter/binaries/
本篇直接点击此链接下载:https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.3.zip 下载后直接解压:
注意Windows后缀为.zip,如:apache-jmeter-5.3.zip
运行:
①打开,如我的解压路径(可创建快捷方式/超链接到桌面):G:\apache-jmeter-5.3\bin\ApacheJMeter.jar
②打开jmeter.jar后的可视化界面:
3、ant:
所有版本:https://downloads.apache.org/ant/binaries/
本篇直接点击此链接下载:https://downloads.apache.org/ant/binaries/apache-ant-1.9.15-bin.zip 下载后直接解压:
注意Windows后缀为.zip,如:apache-ant-1.9.15-bin.zip
设置环境变量:
①将bin、lib目录添加至环境变量中的系统变量path即可,如下图我的解压路径:
D:\apache-ant-1.9.15\bin
D:\apache-ant-1.9.15\lib
②cmd验证环境:
cmd输入:ant -v
如下图即可显示ant的版本为:1.9.15
二、jmeter、ant各种配置
1、jmeter配置:
①将jmeter目录bin\jmeter.properties的490行-528行改为如下:
注意:是jmeter.properties文件,在490-528行中,凡是有jmeter开头的且有#号都需要去掉,或者直接复制下面替换490-528行;
jmeter.save.saveservice.output_format=xml
# The below properties are true when field should be saved; false otherwise
#
# assertion_results_failure_message only affects CSV output
jmeter.save.saveservice.assertion_results_failure_message=true
#
# legitimate values: none, first, all
jmeter.save.saveservice.assertion_results=true
#
jmeter.save.saveservice.data_type=true
jmeter.save.saveservice.label=true
jmeter.save.saveservice.response_code=true
# response_data is not currently supported for CSV output
jmeter.save.saveservice.response_data=true
# Save ResponseData for failed samples
jmeter.save.saveservice.response_data.on_error=true
jmeter.save.saveservice.response_message=true
jmeter.save.saveservice.successful=true
jmeter.save.saveservice.thread_name=true
jmeter.save.saveservice.time=true
jmeter.save.saveservice.subresults=true
jmeter.save.saveservice.assertions=true
jmeter.save.saveservice.latency=true
# Only available with HttpClient4
jmeter.save.saveservice.connect_time=true
jmeter.save.saveservice.samplerData=true
jmeter.save.saveservice.responseHeaders=true
jmeter.save.saveservice.requestHeaders=true
jmeter.save.saveservice.encoding=true
jmeter.save.saveservice.bytes=true
# Only available with HttpClient4
jmeter.save.saveservice.sent_bytes=true
jmeter.save.saveservice.url=true
jmeter.save.saveservice.filename=true
jmeter.save.saveservice.hostname=true
jmeter.save.saveservice.thread_counts=true
jmeter.save.saveservice.sample_count=true
jmeter.save.saveservice.idle_time=true
2、ant 配置
①将apache-jmeter-5.3\extras目录下的:ant-jmeter-1.1.1.jar 复制到apache-ant-1.9.15\lib\
②创建以下目录文件(网盘下载的不需要再新建了):
推荐直接网盘下载完整文件夹,链接:https://pan.baidu.com/s/1dKy6GpHFDMBIYK0qQixdiA 提取码:gsxl
html、jtl、xsl为空文件夹,
build.xml复制我以下代码:
jmx为jmeter脚本,
可以直接复制我的:
jmeter.results.shanhe.me.xsl(放在xsl文件夹下):
1 <?xml version="1.0" encoding="UTF-8"?>
2 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
3 <xsl:output method="html" indent="no" encoding="UTF-8" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" doctype-system="http://www.w3.org/TR/html4/loose.dtd"/>
4 <xsl:strip-space elements="*"/>
5 <xsl:template match="/testResults">
6 <html lang="en">
7 <head>
8 <meta name="Author" content="shanhe.me"/>
9 <title>JMeter接口测试报告</title>
10 <style type="text/css"><![CDATA[
11
12 * { margin: 0; padding: 0 }
13 html, body { width: 100%; height: 100%; background: #b4b4b4; font-size: 12px }
14 table { border: none; border-collapse: collapse; table-layout: fixed }
15 td { vertical-align: baseline; font-size: 12px }
16 #left-panel { position: absolute; left: 0; top: 0; bottom: 0; width: 300px; overflow: auto; background: #dee4ea }
17 #left-panel li.navigation { font-weight: bold; cursor: default; color: #9da8b2; line-height: 18px; background-position: 12px 5px; background-repeat: no-repeat; padding: 0 0 0 25px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAICAYAAAArzdW1AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBQqGbO7BEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAKRJREFUGNN1zM0KgkAYheF3RvtXSsGyWhRNaILS7bdt11W0KgJvoPwZp0UlBPUtz3nOJw7Hk7necv5dOA2Qaazo2vZP0LEt9olCVtqQROufKNmuqBuBNAYW4QzXGX6B0bDPcjGnMQYJ8Cg12U59oSzaUJQa4IUAXMclDHwAAn/MxPMw765FZd2QRgopBWmsKCrdfhXnS/4ZYElBXdyxewN008Y8AephLAkqz613AAAAAElFTkSuQmCC) }
18 #left-panel li.success { color: #565b60 }
19 #left-panel li.failure { color: red }
20 #left-panel li { list-style: none; color: black; cursor: pointer }
21 #left-panel li.selected { background-repeat: repeat-x; color: white; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAUCAYAAABMDlehAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBQxLTs5O2gAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAEdJREFUCNc1y7ERgEAMA0GNUhIyGqM2uqKgtyWZhE9v53A/7/A6D7BkMDNgy2AroB2wHTCZv5UMOgFLG1bvd7XBckBlwCXjA5wMOF5iOX/MAAAAAElFTkSuQmCC) }
22 #left-panel div { line-height: 20px; background-position: 25px 3px; background-repeat: no-repeat; padding: 0 0 0 45px }
23 #left-panel div.success { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAYAAADwikbvAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBULEEc6wzcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAiNJREFUKM99kktIVGEYhp/jzJl08lI6logp2Y2EFkbtaqlFROsWrlq4ioJWQRs37VoUVItWkYEVRGSBlhleCpywDEWxTEuxcURTZ6YzxzP/5WshCOHUt36f93kXnyMi5Lsnb4clI4s4fhkXzp5w8mWcfHBvfEpUxVdCUUU6lUPNHuD86cYtBQX5GhPrM7hRg7GaSDRg2vuUd90WuOPVsOyqy6FFo2yOQHlU1S9z9dZT+S/8I7GCLlkAN4eyAf56mnT6Fy1HLnGuuYa++MS/4e74qMRqfXLaJ9BpfnsrLC0m2BYuoqwUbj/+274JD43OEqmexwvW8NUKXnaZtVSS1pNtAAyOvyC6v48HnUNb4Z7PH8UtTlIQWA5tb2RhYY7kz3l2FleytJYg/qWb8t2KZ/0PN+1hgI6uEUr2jpHKpGlquExVaS0VbjUZL7WxaqIXK6ADQ0n9GNfv9XCttWnD/O57t0TKFklnF3g5fJ/seoaa2D4O1x0F4PlgO9oIftbgFgYMfLgjACGqj0vlsddoUnj+Kt/mxunq72RP+UGqYjWMTA7R+b6dUCSEGEF5hoJQip6BaFs4HJtCyRrKs6wHCovDip/kys0WWpovMpOYBCtoT2N9B5uzWG0Zid8gnFrVFEQDtBaUrxEgXBimaEeER2/uIiK4roPOaMRYjBKsFly3fOO3G06dETGCWIsYjckprMphtEKMAQtgsMYi1mJMQHJ6xvkDKQoyphCzkl0AAAAASUVORK5CYII=) }
24 #left-panel div.failure { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAYAAADwikbvAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBUJOEC5CU8AAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAeVJREFUKM+NkDtok2EUhp8vl9ZLo/EyKI6KFgqCKC4OClrBWUQEcRRx1cGpk3WyInWrgoMZKkW8thYaEYQ0i7WC2ngrNDTERHJvkv/L/3//dxwc7F8jeOAsh/c973OOEhG61aPnaen7maXYt4MLZ4+pbppQt+F06jNH3QWOb8pxUs+SmJzjv83hxY8SVy3wNdtVneiHqe54IhLoB4/TUkyMyOrKj5yXoVtPZK02kLyYK7OnlqFWzgcCGtUC/YUJ3n5a/jd28tU7ORTN0myUA6Jms8bpWIa798elqzn1fokjThrpVBC3ETzNbYAuca59j/Hp+b/N869Tsk8tgVMCXQk+RlfQuk1/tMLMwzsSMCcm5zjhvoR2AdpF0GuwO4aqttS05ZSbZHhsBoAIwI83Cdkd/460XDAOG02d24MxvlR8dsUUh3f2UHaEtgdbWCHz4oZwcVCp66PP5FLhKjEc8DXaCMsNy8DYn/SnZ+L0hhWOb/F8yLs9fDtwk8j+VpqwrlC34PrgGEu2bhlYhZ1b8dncq3AMeBaUr/k6NUyk4ChKzu+N2hc6Bqody+WDG8g2fLatD7F3axjPgmvAtYJvIbouhhIRrl0ZktnkBGIt1gqeMXQ8D2MMiCIUCqFEsFhEQMSykCuqX0MzLAUJTzRsAAAAAElFTkSuQmCC) }
25 #left-panel div.detail { display: none }
26 #right-panel { position: absolute; right: 0; top: 0; bottom: 0; left: 301px; overflow: auto; background: white }
27 #right-panel .group { font-size: 12px; font-weight: bold; line-height: 16px; padding: 0 0 0 18px; counter-reset: assertion; background-repeat: repeat-x; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAQCAYAAADXnxW3AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBUkDq8pxjkAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAADdJREFUCNdVxrERwDAMAzGK0v47eS6Z927SpMFBAAbkvSvnRk5+7K5cVfLMyN39bWakJAjA5xw9R94jN3tVhVEAAAAASUVORK5CYII=) }
28 #right-panel .zebra { background-repeat: repeat; padding: 0 0 0 18px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAmCAYAAAAFvPEHAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBYWFlNztEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAABdJREFUCNdjYKAtePv5338mBgYGBpoQAGy1BAJlb/y6AAAAAElFTkSuQmCC) }
29 #right-panel .data { line-height: 19px; white-space: nowrap }
30 #right-panel pre.data { white-space: pre }
31 #right-panel tbody.failure { color: red }
32 #right-panel td.key { min-width: 108px }
33 #right-panel td.delimiter { min-width: 18px }
34 #right-panel td.assertion:before { counter-increment: assertion; content: counter(assertion) ". " }
35 #right-panel td.assertion { color: black }
36 #right-panel .trail { border-top: 1px solid #b4b4b4 }
37
38 <!--JSON响应值格式化所用样式 -->
39 div.ControlsRow, div.HeadersRow {font-family: Georgia;}
40 div.Canvas{font-family: Lucida Console, Georgia;font-size: 13px;background-color:#ECECEC;color:#000000;border:solid 1px #CECECE;}
41 .ObjectBrace{color:#00AA00;font-weight:bold;}
42 .ArrayBrace{color:#0033FF;font-weight:bold;}
43 .PropertyName{color:#CC0000;font-weight:bold;}
44 .String{color:#007777;}
45 .Number{color:#AA00AA;}
46 .Boolean{color:#0000FF;}
47 .Function{color:#AA6633;text-decoration:italic;}
48 .Null{color:#0000FF;}
49 .Comma{color:#000000;font-weight:bold;}
50 PRE.CodeContainer{margin-top:0px;margin-bottom:0px;}
51 PRE.CodeContainer img{cursor:pointer;border:none;margin-bottom:-1px;}
52 #CollapsibleViewDetail a{padding-left:10px;}
53 #ControlsRow{white-space:nowrap;font: 11px Georgia;}
54 #TabSizeHolder{padding-left:10px;padding-right:10px;}
55 #HeaderTitle{text-align:right;font-size:11px;}
56 #HeaderSubTitle{margin-bottom:2px;margin-top:0px}
57 A.OtherToolsLink {color:#555;text-decoration:none;}
58 A.OtherToolsLink:hover {text-decoration:underline;}
59 #alldata {table-layout:fixed;empty-cells:show;border-collapse: collapse;margin:0 auto;font-size:12px;border:1px solid #cad9ea;color:#4f6b72;}
60 #alldata th{background-repeat:repeat-x;height:30px;border:1px solid #cad9ea;background-color:#EDF3FE;}
61 #alldata td{border:1px solid #cad9ea;padding:0 1em 0;}
62 #alldata2 {table-layout:fixed;empty-cells:show;border-collapse: collapse;margin:0 auto;font-size:12px;border:1px solid #cad9ea;color:#4f6b72;}
63 #alldata2 th{background-repeat:repeat-x;height:30px;border:1px solid #cad9ea;}
64 #alldata2 td{border:1px solid #cad9ea;padding:0 1em 0;}
65 #alldata2 tr:nth-child(even){background-color:#EDF3FE;}
66
67
68 ]]></style>
69 <script type="text/javascript"><![CDATA[
70
71 var onclick_li = (function() {
72 var last_selected = null;
73 return function(li, index) {
74 Process(index);
75 if( last_selected == li )
76 return;
77 if( last_selected )
78 last_selected.className = "";
79 last_selected = li;
80 last_selected.className = "selected";
81 document.getElementById("right-panel").innerHTML = last_selected.firstChild.nextSibling.innerHTML;
82 return false;
83 };
84 })();
85
86 var patch_timestamp = function() {
87 var spans = document.getElementsByTagName("span");
88 var len = spans.length;
89 for( var i = 0; i < len; ++i ) {
90 var span = spans[i];
91 if( "patch_timestamp" == span.className )
92 span.innerHTML = new Date( parseInt( span.innerHTML ) );
93 }
94 };
95
96 var patch_navigation_class = (function() {
97
98 var set_class = function(el, flag) {
99 if(el) {
100 el.className += flag ? " success" : " failure";
101 }
102 };
103
104 var traverse = function(el, group_el, flag) {
105 while(1) {
106 if(el) {
107 if(el.className == 'navigation') {
108 set_class(group_el, flag);
109 group_el = el;
110 flag = true;
111 } else {
112 var o = el.firstChild;
113 o = o ? o.className : null;
114 flag = flag ? (o == 'success') : false;
115 }
116 el = el.nextSibling;
117 } else {
118 set_class(group_el, flag);
119 break;
120 }
121 }
122 };
123
124 return function() {
125 var o = document.getElementById("result-list");
126 o = o ? o.firstChild : null;
127 if(o)
128 traverse(o, null, true);
129 };
130 })();
131
132 window.onload = function() {
133 patch_timestamp();
134 patch_navigation_class();
135 var o = document.getElementById("result-all");
136 o = o ? o.firstChild : null;
137 o = o ? o.nextSibling : null;
138 if(o)
139 onclick_li(o, 1);
140 };
141
142
143 //JSON 格式化引用的js
144 window.SINGLE_TAB = " ";
145 window.QuoteKeys = true;
146 function $id(id){ return document.getElementById(id); }
147 function IsArray(obj) {
148 return obj &&
149 typeof obj === 'object' &&
150 typeof obj.length === 'number' &&
151 !(obj.propertyIsEnumerable('length'));
152 }
153
154 function Process(index){
155 SetTab();
156 window.IsCollapsible = false;
157 debugger;
158 if (!index){
159 return false;
160 }
161 var json = $id("RawJson" + index).textContent;
162
163 var html = "";
164 try{
165 if(json == "") json = "\"\"";
166 var obj = eval("["+json+"]");
167 html = ProcessObject(obj[0], 0, false, false, false);
168 $id("Canvas" + index).innerHTML = "<PRE class='CodeContainer'>"+html+"</PRE>";
169 }catch(e){
170 //alert("JSON数据格式不正确:\n"+e.message);
171 $id("Canvas" + index).innerHTML = "not Json Data";
172 }
173 }
174 window._dateObj = new Date();
175 window._regexpObj = new RegExp();
176 function ProcessObject(obj, indent, addComma, isArray, isPropertyContent){
177
178 var html = "";
179 var comma = (addComma) ? "<span class='Comma'>,</span> " : "";
180 var type = typeof obj;
181 var clpsHtml ="";
182 if(IsArray(obj)){
183 if(obj.length == 0){
184 html += GetRow(indent, "<span class='ArrayBrace'>[ ]</span>"+comma, isPropertyContent);
185 }else{
186 clpsHtml = window.IsCollapsible ? "<span><img src=\""+window.ImgExpanded+"\" onClick=\"ExpImgClicked(this)\" /></span><span class='collapsible'>" : "";
187 html += GetRow(indent, "<span class='ArrayBrace'>[</span>"+clpsHtml, isPropertyContent);
188 for(var i = 0; i < obj.length; i++){
189 html += ProcessObject(obj[i], indent + 1, i < (obj.length - 1), true, false);
190 }
191 clpsHtml = window.IsCollapsible ? "</span>" : "";
192 html += GetRow(indent, clpsHtml+"<span class='ArrayBrace'>]</span>"+comma);
193 }
194 }else if(type == 'object'){
195 if (obj == null){
196 html += FormatLiteral("null", "", comma, indent, isArray, "Null");
197 }else if (obj.constructor == window._dateObj.constructor) {
198 html += FormatLiteral("new Date(" + obj.getTime() + ") /*" + obj.toLocaleString()+"*/", "", comma, indent, isArray, "Date");
199 }else if (obj.constructor == window._regexpObj.constructor) {
200 html += FormatLiteral("new RegExp(" + obj + ")", "", comma, indent, isArray, "RegExp");
201 }else{
202 var numProps = 0;
203 for(var prop in obj) numProps++;
204 if(numProps == 0){
205 html += GetRow(indent, "<span class='ObjectBrace'>{ }</span>"+comma, isPropertyContent);
206 }else{
207 clpsHtml = window.IsCollapsible ? "<span><img src=\""+window.ImgExpanded+"\" onClick=\"ExpImgClicked(this)\" /></span><span class='collapsible'>" : "";
208 html += GetRow(indent, "<span class='ObjectBrace'>{</span>"+clpsHtml, isPropertyContent);
209
210 var j = 0;
211
212 for(var prop in obj){
213
214 var quote = window.QuoteKeys ? "\"" : "";
215
216 html += GetRow(indent + 1, "<span class='PropertyName'>"+quote+prop+quote+"</span>: "+ProcessObject(obj[prop], indent + 1, ++j < numProps, false, true));
217
218 }
219
220 clpsHtml = window.IsCollapsible ? "</span>" : "";
221
222 html += GetRow(indent, clpsHtml+"<span class='ObjectBrace'>}</span>"+comma);
223
224 }
225
226 }
227
228 }else if(type == 'number'){
229
230 html += FormatLiteral(obj, "", comma, indent, isArray, "Number");
231
232 }else if(type == 'boolean'){
233
234 html += FormatLiteral(obj, "", comma, indent, isArray, "Boolean");
235
236 }else if(type == 'function'){
237
238 if (obj.constructor == window._regexpObj.constructor) {
239
240 html += FormatLiteral("new RegExp(" + obj + ")", "", comma, indent, isArray, "RegExp");
241
242 }else{
243
244 obj = FormatFunction(indent, obj);
245
246 html += FormatLiteral(obj, "", comma, indent, isArray, "Function");
247
248 }
249
250 }else if(type == 'undefined'){
251
252 html += FormatLiteral("undefined", "", comma, indent, isArray, "Null");
253
254 }else{
255
256 html += FormatLiteral(obj.toString().split("\\").join("\\\\").split('"').join('\\"'), "\"", comma, indent, isArray, "String");
257
258 }
259
260 return html;
261
262 }
263
264 function FormatLiteral(literal, quote, comma, indent, isArray, style){
265
266 if(typeof literal == 'string')
267 literal = literal.split("<").join("<").split(">").join(">");
268 var str = "<span class='"+style+"'>"+quote+literal+quote+comma+"</span>";
269 if(isArray) str = GetRow(indent, str);
270 return str;
271
272 }
273
274 function FormatFunction(indent, obj){
275 var tabs = "";
276 for(var i = 0; i < indent; i++) tabs += window.TAB;
277 var funcStrArray = obj.toString().split("\n");
278 var str = "";
279 for(var i = 0; i < funcStrArray.length; i++){
280 str += ((i==0)?"":tabs) + funcStrArray[i] + "\n";
281 }
282 return str;
283 }
284
285 function GetRow(indent, data, isPropertyContent){
286 var tabs = "";
287 for(var i = 0; i < indent && !isPropertyContent; i++) tabs += window.TAB;
288 if(data != null && data.length > 0 && data.charAt(data.length-1) != "\n")
289 data = data+"\n";
290 return tabs+data;
291 }
292
293 function TraverseChildren(element, func, depth){
294 for(var i = 0; i < element.childNodes.length; i++){
295 TraverseChildren(element.childNodes[i], func, depth + 1);
296 }
297 func(element, depth);
298 }
299
300 function SetTab(){
301 window.TAB = MultiplyString(2, window.SINGLE_TAB);
302 }
303
304 function MultiplyString(num, str){
305 var sb =[];
306 for(var i = 0; i < num; i++){
307 sb.push(str);
308 }
309 return sb.join("");
310 }
311
312 ]]></script>
313
314 </head>
315 <body>
316 <div id="left-panel">
317 <ol id="result-all">
318 <xsl:for-each select="*[position() =1]">
319 <!-- group with the previous sibling -->
320 <xsl:if test="position() = 1 or @tn != preceding-sibling::*[1]/@tn">
321 <li class="navigation">测试名称</li>
322 </xsl:if>
323 <li onclick="return onclick_li(this, {position()});">
324 <div>
325
326 <xsl:attribute name="class">
327 <xsl:choose>
328 <xsl:when test="count(/testResults/*[attribute::s='false']) = 0">success</xsl:when>
329 <xsl:otherwise>failure</xsl:otherwise>
330 </xsl:choose>
331 </xsl:attribute>
332 <xsl:value-of select="string('jmeter接口自动化测试')"/>
333 </div>
334 <div class="detail">
335 <div class="group">统计</div>
336 <br/>
337 <table id="alldata" align="center" border="0" cellpadding="5" cellspacing="2" width="95%">
338 <tr valign="top">
339 <th>所有</th>
340 <th>失败</th>
341 <th>成功率</th>
342 <th>平均时间</th>
343 <th>最小时间</th>
344 <th>最大时间</th>
345 </tr>
346 <tr valign="top">
347 <xsl:variable name="allCount" select="count(/testResults/*)" />
348 <xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s='false'])" />
349 <xsl:variable name="allSuccessCount" select="count(/testResults/*[attribute::s='true'])" />
350 <xsl:variable name="allSuccessPercent" select="$allSuccessCount div $allCount" />
351 <xsl:variable name="allTotalTime" select="sum(/testResults/*/@t)" />
352 <xsl:variable name="allAverageTime" select="$allTotalTime div $allCount" />
353 <xsl:variable name="allMinTime">
354 <xsl:call-template name="min">
355 <xsl:with-param name="nodes" select="/testResults/*/@t" />
356 </xsl:call-template>
357 </xsl:variable>
358 <xsl:variable name="allMaxTime">
359 <xsl:call-template name="max">
360 <xsl:with-param name="nodes" select="/testResults/*/@t" />
361 </xsl:call-template>
362 </xsl:variable>
363 <xsl:attribute name="class">
364 <xsl:choose>
365 <xsl:when test="$allFailureCount > 0">Failure</xsl:when>
366 </xsl:choose>
367 </xsl:attribute>
368 <td align="center">
369 <xsl:value-of select="$allCount" />
370 </td>
371 <td align="center">
372 <xsl:value-of select="$allFailureCount" />
373 </td>
374 <td align="center">
375 <xsl:call-template name="display-percent">
376 <xsl:with-param name="value" select="$allSuccessPercent" />
377 </xsl:call-template>
378 </td>
379 <td align="center">
380 <xsl:call-template name="display-time">
381 <xsl:with-param name="value" select="$allAverageTime" />
382 </xsl:call-template>
383 </td>
384 <td align="center">
385 <xsl:call-template name="display-time">
386 <xsl:with-param name="value" select="$allMinTime" />
387 </xsl:call-template>
388 </td>
389 <td align="center">
390 <xsl:call-template name="display-time">
391 <xsl:with-param name="value" select="$allMaxTime" />
392 </xsl:call-template>
393 </td>
394 </tr>
395 </table>
396 <br/>
397 <div class="trail"></div>
398
399 <div class="group">统计详情</div>
400 <br/>
401 <table id="alldata2" align="center" border="0" cellpadding="5" cellspacing="2" width="95%">
402 <tr valign="top">
403 <th>接口名称</th>
404 <th>所有</th>
405 <th>失败</th>
406 <th>成功率</th>
407 <th>平均时间</th>
408 <th>最小时间</th>
409 <th>最大时间</th>
410 </tr>
411 <xsl:for-each select="/testResults/*[not(@lb = preceding::*/@lb)]">
412 <xsl:variable name="label" select="@lb" />
413 <xsl:variable name="count" select="count(../*[@lb = current()/@lb])" />
414 <xsl:variable name="failureCount" select="count(../*[@lb = current()/@lb][attribute::s='false'])" />
415 <xsl:variable name="successCount" select="count(../*[@lb = current()/@lb][attribute::s='true'])" />
416 <xsl:variable name="successPercent" select="$successCount div $count" />
417 <xsl:variable name="totalTime" select="sum(../*[@lb = current()/@lb]/@t)" />
418 <xsl:variable name="averageTime" select="$totalTime div $count" />
419 <xsl:variable name="minTime">
420 <xsl:call-template name="min">
421 <xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" />
422 </xsl:call-template>
423 </xsl:variable>
424 <xsl:variable name="maxTime">
425 <xsl:call-template name="max">
426 <xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" />
427 </xsl:call-template>
428 </xsl:variable>
429 <tr valign="top">
430 <xsl:attribute name="class">
431 <xsl:choose>
432 <xsl:when test="$failureCount > 0">Failure</xsl:when>
433 </xsl:choose>
434 </xsl:attribute>
435 <td>
436 <xsl:if test="$failureCount > 0">
437 <a><xsl:attribute name="href">#<xsl:value-of select="$label" /></xsl:attribute>
438 <xsl:value-of select="$label" />
439 </a>
440 </xsl:if>
441 <xsl:if test="0 >= $failureCount">
442 <xsl:value-of select="$label" />
443 </xsl:if>
444 </td>
445 <td align="center">
446 <xsl:value-of select="$count" />
447 </td>
448 <td align="center">
449 <xsl:value-of select="$failureCount" />
450 </td>
451 <td align="center">
452 <xsl:call-template name="display-percent">
453 <xsl:with-param name="value" select="$successPercent" />
454 </xsl:call-template>
455 </td>
456 <td align="center">
457 <xsl:call-template name="display-time">
458 <xsl:with-param name="value" select="$averageTime" />
459 </xsl:call-template>
460 </td>
461 <td align="center">
462 <xsl:call-template name="display-time">
463 <xsl:with-param name="value" select="$minTime" />
464 </xsl:call-template>
465 </td>
466 <td align="center">
467 <xsl:call-template name="display-time">
468 <xsl:with-param name="value" select="$maxTime" />
469 </xsl:call-template>
470 </td>
471
472 </tr>
473 </xsl:for-each>
474 </table>
475 </div>
476 </li>
477 </xsl:for-each>
478 </ol>
479 <ol id="result-list">
480 <xsl:for-each select="*">
481 <!-- group with the previous sibling -->
482 <xsl:if test="position() = 1 or @tn != preceding-sibling::*[1]/@tn">
483 <li class="navigation">线程名称: <xsl:value-of select="@tn"/></li>
484 </xsl:if>
485 <li onclick="return onclick_li(this, {position()});">
486 <div>
487 <xsl:attribute name="class">
488 <xsl:choose>
489 <xsl:when test="@s = 'true'">success</xsl:when>
490 <xsl:otherwise>failure</xsl:otherwise>
491 </xsl:choose>
492 </xsl:attribute>
493 <xsl:value-of select="@lb"/>
494 </div><div class="detail">
495 <div class="group">请求详情</div>
496 <div class="zebra">
497 <table>
498 <tr><td class="data key">线程名称</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@tn"/></td></tr>
499 <tr><td class="data key">时间</td><td class="data delimiter">:</td><td class="data"><span class="patch_timestamp"><xsl:value-of select="@ts"/></span></td></tr>
500 <tr><td class="data key">消化时间</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@t"/> ms</td></tr>
501 <tr><td class="data key">延迟</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@lt"/> ms</td></tr>
502 <tr><td class="data key">字节</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@by"/></td></tr>
503 <tr><td class="data key">请求次数</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@sc"/></td></tr>
504 <tr><td class="data key">失败次数</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@ec"/></td></tr>
505 <tr><td class="data key">响应状态码</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rc"/></td></tr>
506 <tr><td class="data key">响应消息</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rm"/></td></tr>
507 </table>
508 </div>
509 <div class="trail"></div>
510 <xsl:if test="count(assertionResult) > 0">
511 <div class="group">断言</div>
512 <div class="zebra">
513 <table>
514 <xsl:for-each select="assertionResult">
515 <tbody>
516 <xsl:attribute name="class">
517 <xsl:choose>
518 <xsl:when test="failure = 'true'">failure</xsl:when>
519 <xsl:when test="error = 'true'">failure</xsl:when>
520 </xsl:choose>
521 </xsl:attribute>
522 <tr><td class="data assertion" colspan="3"><xsl:value-of select="name"/></td></tr>
523 <tr><td class="data key">是否断言失败</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failure"/></td></tr>
524 <tr><td class="data key">是否断言错误</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="error"/></td></tr>
525 <tr><td class="data key">断言失败消息</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failureMessage"/></td></tr>
526 </tbody>
527 </xsl:for-each>
528 </table>
529 </div>
530 <div class="trail"></div>
531 </xsl:if>
532 <div class="group">请求信息</div>
533 <div class="zebra">
534 <table>
535 <tr><td class="data key">Method/Url</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="method"/><xsl:text> </xsl:text><xsl:value-of select="java.net.URL"/></pre></td></tr>
536 <tr><td class="data key">Query String</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="queryString"/></pre></td></tr>
537 <tr><td class="data key">Cookies</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="cookies"/></pre></td></tr>
538 <tr><td class="data key">请求头Headers</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="requestHeader"/></pre></td></tr>
539 </table>
540 </div>
541 <div class="trail"></div>
542 <div class="group">响应信息</div>
543 <div class="zebra">
544 <table>
545 <tr><td class="data key">响应-Headers</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseHeader"/></pre></td></tr>
546 <tr><td class="data key">响应body</td><td class="data delimiter">:</td><td class="data"><pre id='RawJson{position()}' class="data"><xsl:value-of select="responseData"/></pre></td></tr>
547 <tr><td class="data key">Response File</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseFile"/></pre></td></tr>
548 <tr><td class="data key">JSON 格式化</td><td class="data delimiter">:</td><td><div id="Canvas{position()}" class="Canvas"></div></td></tr>
549 </table>
550 </div>
551 <div class="trail"></div>
552 </div>
553 </li>
554 </xsl:for-each>
555 </ol>
556 </div>
557 <div id="right-panel"></div>
558 </body>
559 </html>
560 </xsl:template>
561 <xsl:template name="min">
562 <xsl:param name="nodes" select="/.." />
563 <xsl:choose>
564 <xsl:when test="not($nodes)">NaN</xsl:when>
565 <xsl:otherwise>
566 <xsl:for-each select="$nodes">
567 <xsl:sort data-type="number" />
568 <xsl:if test="position() = 1">
569 <xsl:value-of select="number(.)" />
570 </xsl:if>
571 </xsl:for-each>
572 </xsl:otherwise>
573 </xsl:choose>
574 </xsl:template>
575
576 <xsl:template name="max">
577 <xsl:param name="nodes" select="/.." />
578 <xsl:choose>
579 <xsl:when test="not($nodes)">NaN</xsl:when>
580 <xsl:otherwise>
581 <xsl:for-each select="$nodes">
582 <xsl:sort data-type="number" order="descending" />
583 <xsl:if test="position() = 1">
584 <xsl:value-of select="number(.)" />
585 </xsl:if>
586 </xsl:for-each>
587 </xsl:otherwise>
588 </xsl:choose>
589 </xsl:template>
590
591 <xsl:template name="display-percent">
592 <xsl:param name="value" />
593 <xsl:value-of select="format-number($value,'0.00%')" />
594 </xsl:template>
595
596 <xsl:template name="display-time">
597 <xsl:param name="value" />
598 <xsl:value-of select="format-number($value,'0 ms')" />
599 </xsl:template>
600 </xsl:stylesheet>
build.xml:
1 <?xml version="1.0" encoding="utf-8"?>
2 <project name="ant-jmeter-test" default="run" basedir=".">
3 <taskdef resource="net/sf/antcontrib/antlib.xml"/>
4 <tstamp>
5 <format property="time" pattern="yyyyMMddhhmm" />
6 </tstamp>
7
8
9 <!-- (1)需要改成自己本地的 Jmeter 目录-->
10 <property name="jmeter.home" value="G:/apache-jmeter-5.3" />
11
12 <!-- (2)jmeter生成jtl格式的结果报告的路径-->
13 <!-- jmeter生成jtl格式的结果报告的路径, value="C:/Users/Administrator/Desktop/jmeter+ant-全家桶/标准目录/jtl"-->
14 <!-- jmeter生成html格式的结果报告的路径,value="C:/Users/Administrator/Desktop/jmeter+ant-全家桶/标准目录/html"-->
15 <property name="jmeter.result.jtl.dir" value="C:/Users/Administrator/Desktop/jmeter+ant-全家桶/标准目录/jtl" />
16 <property name="jmeter.result.html.dir" value="C:/Users/Administrator/Desktop/jmeter+ant-全家桶/标准目录/html" />
17
18 <!-- (3)生成的报告的前缀:value-->
19 <property name="ReportName" value="InterfaceReport"/>
20
21
22 <property name="report.title" value="report"/>
23 <property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/${ReportName}${time}.jtl" />
24 <property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}.html"/>
25 <target name="run">
26 <antcall target="test"/>
27 <antcall target="report"/>
28 </target>
29 <target name="test">
30 <taskdef name="jmeter" classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/>
31 <jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
32
33
34 <!-- (4)声明要运行的脚本。*.jmx 指包含此目录下的所有jmeter脚本,需要改成自己本地的目录-->
35 <testplans dir="C:/Users/Administrator/Desktop/jmeter+ant-全家桶/标准目录" includes="*.jmx" />
36
37
38 <property name="jmeter.save.saveservice.output_format" value="xml"/>
39 </jmeter>
40 </target>
41 <path id="xslt.classpath">
42 <fileset dir="${jmeter.home}/lib" includes="xalan*.jar"/>
43 <fileset dir="${jmeter.home}/lib" includes="serializer*.jar"/>
44 </path>
45 <target name="report">
46 <tstamp>
47 <format property="report.datestamp" pattern="yyyy/MM/dd HH:mm"/>
48 </tstamp>
49 <xslt classpathref="xslt.classpath"
50 force="true" in="${jmeter.result.jtlName}"
51 out="${jmeter.result.htmlName}"
52
53
54 style="C:/Users/Administrator/Desktop/jmeter+ant-全家桶/标准目录/xsl/jmeter.results.shanhe.me.xsl">
55 <!--(5)样式目录:/extras/xxx.xsl -->
56
57
58 <param name="dateReport" expression="${report.datestamp}"/>
59 </xslt>
60 <copy todir="${jmeter.result.html.dir}">
61 <fileset dir="${jmeter.home}/extras">
62 <include name="collapse.png"/>
63 <include name="expand.png"/>
64 </fileset>
65 </copy>
66 </target>
67 <path id="lib_classpath">
68 <fileset dir="${basedir}/">
69 <include name="mail*.jar"/>
70 <include name="activation*.jar"/>
71 <include name="commons-email*.jar"/>
72 <include name="ant-contrib*.jar"/>
73 </fileset>
74 </path>
75 </project>
③编辑build.xml:
将你的(1)-(5)都改为自己的路径,保存:
④准备好自己的jmeter脚本放在目录下即可
三、运行ant run
1、在目录下cmd运行
输入:ant run
2、html目录下:查看报告
下一篇将是集成测试,结合我们的jenkins进行,可以继续关注!!!
最后欢迎来大家QQ交流群一起学习:482713805