jmeter-1-apache ant-集成测试
前言:本篇教程是基于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进行,可以继续关注!!!
附加:
Windows搭建传送门:https://www.cnblogs.com/gsxl/p/13655788.html
Linux搭建传送门:https://www.cnblogs.com/gsxl/p/13694071.html
最后欢迎来大家QQ交流群一起学习:482713805
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人