一、数据准备
这里不详细介绍Jmeter的使用方法,重点介绍jmeter与ant的整合
1、添加测试计划-->执行测试用例-->成功后文件另存为.jmx文件
2、在jmeter的目录下新建TestCase文件夹
3、将该.jmx文件存放至该文件夹下
二、工具准备
1、ant的下载地址为: http://ant.apache.org/
2、添加环境变量:新建系统变量ANT_HOME
在path环境变量中加入%ANT_HOME%\bin(注意前面分号)
3、确认ant环境配置,打开cmd命令提示符,输入ant -v,出现如下图说明环境配置OK
三、整合
1、把Jmeter根目录\extras下的ant-jmeter-xxx.jar拷贝到ant的根目录\lib下
To
2、将jmeter的根目录\extras下的build.xml文件复制一份到新建的文件TestCase里去
四、修改配置
1、修改build.xml文件如下
2、修改jmeter.properties配置文件
jmeter.save.saveservice.output_format=csv
改为
jmeter.save.saveservice.output_format=xml
五、生成报告
1、方法一:打开cmd命令提示符,进入build.xml所在目录的根目录,执行ant命令,一份简单的报告就这样生成了。
2、方法二:在build.xml同级目录创建一个build.bat文件,文件内容为ant,然后双击build.bat,jmeter脚本即可自动运行并在指定位置生成对应的测试报告
六、优化报告
上述生成的报告看起来成功,但如出错很难从报告上定位问题,并定位问题在哪里
优化过程:
1、jmeter默认的报告展示信息比较少,其实是由.jtl格式转化为.html格式的报告过程中style文件起了很关键的作用。这里介绍另一种style文件的使用方法:
A) 下载style文件:jmeter.results.shanhe.me.xsl ,该文件可自行网上搜索;
作者使用的是 hph_report.xsl,将上述 build.xml 中 report sytle 值修改为此文件名
B) 把下载的文件放到jmeter的extras目录下 ;
2、修改JMeter.properties文件如下部分,我这里都修改成true,这样执行完脚本后
就会保存这些结果到.jtl文件
1 jmeter.save.saveservice.response_code=true 2 # response_data is not currently supported for CSV output 3 jmeter.save.saveservice.response_data=true 4 # Save ResponseData for failed samples 5 jmeter.save.saveservice.response_data.on_error=false 6 jmeter.save.saveservice.response_message=true 7 jmeter.save.saveservice.successful=true 8 jmeter.save.saveservice.thread_name=true 9 jmeter.save.saveservice.time=true 10 jmeter.save.saveservice.subresults=true 11 jmeter.save.saveservice.assertions=true 12 #jmeter.save.saveservice.latency=true 13 # Only available with HttpClient4 14 jmeter.save.saveservice.connect_time=true 15 jmeter.save.saveservice.samplerData=true 16 jmeter.save.saveservice.responseHeaders=true 17 jmeter.save.saveservice.requestHeaders=true 18 jmeter.save.saveservice.encoding=true 19 #jmeter.save.saveservice.bytes=true 20 # Only available with HttpClient4 21 jmeter.save.saveservice.sent_bytes=true 22 jmeter.save.saveservice.url=true 23 jmeter.save.saveservice.filename=true 24 jmeter.save.saveservice.hostname=true 25 jmeter.save.saveservice.thread_counts=true 26 jmeter.save.saveservice.sample_count=true 27 jmeter.save.saveservice.idle_time=true
3、修改build.xml文件,附上自己的原文件,修改一下自己的 jmeter 环境变量地址
<?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <project name="ant-jmeter" default="all" basedir="."> <tstamp> <format property="time" pattern="yyyy/MM/dd HH:mm" /> <format property="timelog" pattern="yyyyMMddHHmm" /> <format property="timeyj" pattern="MMddyyyHHmm" /> </tstamp> <description> Sample build file for use with ant-jmeter.jar See http://www.programmerplanet.org/pages/projects/jmeter-ant-task.php To run a test and create the output report: ant -Dtest=script To run a test only: ant -Dtest=script run To run report on existing test output ant -Dtest=script report The "script" parameter is the name of the script without the .jmx suffix. Additional options: -Dshow-data=y - include response data in Failure Details -Dtestpath=xyz - path to test file(s) (default user.dir). N.B. Ant interprets relative paths against the build file -Djmeter.home=.. - path to JMeter home directory (defaults to parent of this build file) -Dreport.title="My Report" - title for html report (default is \'Load Test Results\') </description> <property name="jmeter.home" value="这里为 jmeter环境变量目录"/> <property name="testpath" value="${jmeter.home}/TestCase"/> <property name="jmeter.result.jtl.dir" value="${jmeter.home}/TestCase/report/jtl"/> <property name="jmeter.result.html.dir" value="${jmeter.home}/TestCase/report/html"/> <property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/${ReportName}${timelog}.jtl"/> <property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}${timelog}.html"/> <property name="jmeter.result.htmlNameyj" value="${jmeter.result.html.dir}/${ReportName}${timeyj}.html"/> <property name="lib.dir" value="${jmeter.home}/lib"/> <property name="ReportName" value="TestReport"/> <!-- Name of test (without .jmx) --> <property name="test" value="这里填写 jmeter 脚本名称,注意不含.jmx"/> <!-- Should report include response data for failures? --> <property name="show-data" value="n"/> <property name="format" value="2.1"/> <condition property="style_version" value="_21"> <equals arg1="${format}" arg2="2.1"/> </condition> <condition property="funcMode"> <equals arg1="${show-data}" arg2="y"/> </condition> <condition property="funcMode" value="false"> <not> <equals arg1="${show-data}" arg2="y"/> </not> </condition> <!-- Allow jar to be picked up locally (jmeter 相对应 ant-jmeter-1.1.1.jar 包存放地址及包名设置)--> <path id="jmeter.classpath"> <fileset dir="${jmeter.home}/extras"> <include name="ant-jmeter*.jar"/> </fileset> </path> <taskdef name="jmeter" classpathref="jmeter.classpath" classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/> <!--运行的顺序,先 run test 后 report --> <target name="all" depends="run,report"/> <target name="run"> <echo>funcMode = ${funcMode}</echo> <delete file="${jmeter.result.html.dir}/${test}.html"/> <jmeter jmeterhome="${jmeter.home}" testplan ="${testpath}/${test}.jmx" resultlog="${jmeter.result.jtl.dir}/${test}.jtl"> <!-- <jvmarg value="-Xincgc"/> <jvmarg value="-Xmx128m"/> <jvmarg value="-Dproperty=value"/> <jmeterarg value="-qextra.properties"/> --> <!-- Force suitable defaults jmeter相对应配置要改为 true--> <property name="jmeter.save.saveservice.response_data" value="true"/> <property name="jmeter.save.saveservice.samplerData" value="true"/> <property name="jmeter.save.saveservice.responseHeaders" value="true"/> <property name="jmeter.save.saveservice.requestHeaders" value="true"/> <property name="jmeter.save.saveservice.encoding" value="true"/> <property name="jmeter.save.saveservice.url" value="true"/> <property name="jmeter.save.saveservice.filename" value="true"/> <property name="jmeter.save.saveservice.hostname" value="true"/> <property name="jmeter.save.saveservice.thread_counts" value="true"/> <property name="jmeter.save.saveservice.sample_count" value="true"/> <property name="jmeter.save.saveservice.idle_time" value="true"/> <property name="jmeter.save.saveservice.output_format" value="xml"/> <property name="jmeter.save.saveservice.assertion_results" value="all"/> <property name="jmeter.save.saveservice.bytes" value="true"/> <property name="file_format.testlog" value="${format}"/> <property name="jmeter.save.saveservice.response_data.on_error" value="${funcMode}"/> </jmeter> </target> <property name="lib.dir" value="${jmeter.home}/lib"/> <!-- Use xalan copy from JMeter lib directory to ensure consistent processing with Java 1.4+ --> <path id="xslt.classpath"> <fileset dir="${lib.dir}" includes="xalan*.jar"/> <fileset dir="${lib.dir}" includes="serializer*.jar"/> </path> <target name="report" depends="xslt-report,copy-images"> <echo>Report generated at ${report.datestamp}</echo> </target> <!-- 报告样式设置 --> <target name="xslt-report" depends="_message_xalan"> <tstamp><format property="report.datestamp" pattern="yyyy/MM/dd HH:mm"/></tstamp> <xslt classpathref="xslt.classpath" force="true" in="${jmeter.result.jtl.dir}/${test}.jtl" out="${jmeter.result.html.dir}/${test}.html" style="${jmeter.home}/extras/jmeter-results-hph_report.xsl"> <param name="showData" expression="${show-data}"/> <param name="titleReport" expression="${report.title}"/> <param name="dateReport" expression="${report.datestamp}"/> </xslt> </target> <!-- Copy report images if needed --> <target name="copy-images" depends="verify-images" unless="samepath"> <copy file="${basedir}/expand.png" tofile="${testpath}/expand.png"/> <copy file="${basedir}/collapse.png" tofile="${testpath}/collapse.png"/> </target> <target name="verify-images"> <condition property="samepath"> <equals arg1="${testpath}" arg2="${basedir}" /> </condition> </target> <!-- Check that the xalan libraries are present --> <condition property="xalan.present"> <and> <!-- No need to check all jars; just check a few --> <available classpathref="xslt.classpath" classname="org.apache.xalan.processor.TransformerFactoryImpl"/> <available classpathref="xslt.classpath" classname="org.apache.xml.serializer.ExtendedContentHandler"/> </and> </condition> <target name="_message_xalan" unless="xalan.present"> <echo>Cannot find all xalan and/or serialiser jars</echo> <echo>The XSLT formatting may not work correctly.</echo> <echo>Check you have xalan and serializer jars in ${lib.dir}</echo> </target> </project>
将此文件覆盖原jmeter-result-report.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 Test Results</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: 25px; 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: 20px; padding: 0 0 2px 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 20px 18px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAmCAYAAAAFvPEHAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBYWFlNztEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAABdJREFUCNdjYKAtePv5338mBgYGBpoQAGy1BAJlb/y6AAAAAElFTkSuQmCC) } 29 #right-panel .data { line-height: 24px; white-space: nowrap} 30 #right-panel pre.data { white-space: pre-line} 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 ]]></style> 39 <script type="text/javascript"><![CDATA[ 40 41 var onclick_li = (function() { 42 var last_selected = null; 43 return function(li) { 44 if( last_selected == li ) 45 return; 46 if( last_selected ) 47 last_selected.className = ""; 48 last_selected = li; 49 last_selected.className = "selected"; 50 var detail = li.querySelector(\'div.detail\'); 51 // deleted 52 //document.getElementById("right-panel").innerHTML = last_selected.firstChild.nextSibling.innerHTML; 53 // add 54 document.getElementById("right-panel").innerHTML = detail.innerHTML; 55 return false; 56 }; 57 })(); 58 59 var patch_timestamp = function() { 60 var spans = document.getElementsByTagName("span"); 61 var len = spans.length; 62 for( var i = 0; i < len; ++i ) { 63 var span = spans[i]; 64 if( "patch_timestamp" == span.className ) 65 span.innerHTML = new Date( parseInt( span.innerHTML ) ); 66 } 67 }; 68 69 var patch_navigation_class = (function() { 70 71 var set_class = function(el, flag) { 72 if(el) { 73 el.className += flag ? " success" : " failure"; 74 } 75 }; 76 77 var traverse = function(el, group_el, flag) { 78 while(1) { 79 if(el) { 80 if(el.className == \'navigation\') { 81 set_class(group_el, flag); 82 group_el = el; 83 flag = true; 84 } else { 85 var o = el.firstChild; 86 o = o ? o.className : null; 87 flag = flag ? (o == \'success\') : false; 88 } 89 el = el.nextSibling; 90 } else { 91 set_class(group_el, flag); 92 break; 93 } 94 } 95 }; 96 97 return function() { 98 var o = document.getElementById("result-list"); 99 o = o ? o.firstChild : null; 100 if(o) 101 traverse(o, null, true); 102 }; 103 })(); 104 105 window.onload = function() { 106 patch_timestamp(); 107 patch_navigation_class(); 108 // deleted 109 //var o = document.getElementById("result-list"); 110 //o = o ? o.firstChild : null; 111 //o = o ? o.nextSibling : null; 112 // add 113 var o = document.querySelector(\'li[onclick]\'); 114 if(o) 115 onclick_li(o); 116 }; 117 118 ]]></script> 119 </head> 120 <body> 121 <div id="left-panel"> 122 <ol id="result-list"> 123 <li onclick="return onclick_li(this);"> 124 <div>测试结果概况</div> 125 <div class="detail"> 126 <div class="zebra"> 127 <xsl:call-template name="summary" /> 128 </div> 129 <div class="zebra"> 130 <xsl:call-template name="pagelist" /> 131 </div> 132 </div> 133 </li> 134 <xsl:for-each select="*"> 135 <!-- group with the previous sibling --> 136 <!-- 137 <xsl:if test="position() = 1 or @tn != preceding-sibling::*[1]/@tn"> 138 <li class="navigation">Thread: <xsl:value-of select="@tn"/></li> 139 </xsl:if> 140 --> 141 <li onclick="return onclick_li(this);"> 142 <div> 143 <xsl:attribute name="class"> 144 <xsl:choose> 145 <xsl:when test="@s = \'true\'">success</xsl:when> 146 <xsl:otherwise>failure</xsl:otherwise> 147 </xsl:choose> 148 </xsl:attribute> 149 <xsl:value-of select="@lb"/> 150 </div><div class="detail"> 151 <div class="group">Sampler</div> 152 <div class="zebra"> 153 <table> 154 <tr><td class="data key">Thread Name</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@tn"/></td></tr> 155 <tr><td class="data key">Timestamp</td><td class="data delimiter">:</td><td class="data"><span class="patch_timestamp"><xsl:value-of select="@ts"/></span></td></tr> 156 <tr><td class="data key">Time</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@t"/> ms</td></tr> 157 <tr><td class="data key">Latency</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@lt"/> ms</td></tr> 158 <tr><td class="data key">Bytes</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@by"/></td></tr> 159 <tr><td class="data key">Sample Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@sc"/></td></tr> 160 <tr><td class="data key">Error Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@ec"/></td></tr> 161 <tr><td class="data key">Response Code</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rc"/></td></tr> 162 <tr><td class="data key">Response Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rm"/></td></tr> 163 </table> 164 </div> 165 <div class="trail"></div> 166 <xsl:if test="count(assertionResult) > 0"> 167 <div class="group">Assertion</div> 168 <div class="zebra"> 169 <table> 170 <xsl:for-each select="assertionResult"> 171 <tbody> 172 <xsl:attribute name="class"> 173 <xsl:choose> 174 <xsl:when test="failure = \'true\'">failure</xsl:when> 175 <xsl:when test="error = \'true\'">failure</xsl:when> 176 </xsl:choose> 177 </xsl:attribute> 178 <tr><td class="data assertion" colspan="3"><xsl:value-of select="name"/></td></tr> 179 <tr><td class="data key">Failure</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failure"/></td></tr> 180 <tr><td class="data key">Error</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="error"/></td></tr> 181 <tr><td class="data key">Failure Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failureMessage"/></td></tr> 182 </tbody> 183 </xsl:for-each> 184 </table> 185 </div> 186 <div class="trail"></div> 187 </xsl:if> 188 <div class="group">Request</div> 189 <div class="zebra"> 190 <table> 191 <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> 192 <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> 193 <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> 194 <tr><td class="data key">Request Headers</td><td class="data delimiter">:</td><td class="data" ><pre class="data"><xsl:value-of select="requestHeader"/></pre></td></tr> 195 </table> 196 </div> 197 <div class="trail"></div> 198 <div class="group">Response</div> 199 <div class="zebra"> 200 <table> 201 <tr><td class="data key">Response Headers</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseHeader"/></pre></td></tr> 202 <tr><td class="data key">Response Data</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseData"/></pre></td></tr> 203 <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> 204 </table> 205 </div> 206 <div class="trail"></div> 207 </div> 208 </li> 209 </xsl:for-each> 210 </ol> 211 </div> 212 <div id="right-panel"></div> 213 <script> 214 Spliter = function (objLeft, objRight) { 215 this.flag = false; 216 var ele = document.createElement(\'div\'); 217 document.body.append(ele); 218 ele.style.height = "100%"; 219 ele.style.position = "absolute"; 220 ele.style.width = "5px"; 221 ele.style.left = objLeft.offsetWidth - 2 + "px"; 222 ele.style.backgroundColor = "gray"; 223 ele.style.opacity = 0; 224 225 this.ele = ele 226 this.objLeft = objLeft; 227 this.objRight = objRight; 228 this.ele.style.cursor = "w-resize"; //设置鼠标样式 229 this.up = function (event) { 230 if (this.flag) { 231 if (this.ele.releaseCapture) { 232 this.ele.releaseCapture(); 233 } 234 else { 235 document.removeEventListener(\'mouseup\', this.up.bind(this), true); 236 document.removeEventListener(\'mousemove\', this.move.bind(this), true); 237 event.preventDefault(); 238 } 239 240 this.ele.style.opacity = 0; 241 this.flag = false; 242 } 243 }; 244 this.down = function (event) { 245 if (!this.flag) { 246 if (this.ele.setCapture) 247 this.ele.setCapture(); 248 else { 249 document.addEventListener(\'mouseup\', this.up.bind(this), true); 250 document.addEventListener(\'mousemove\', this.move.bind(this), true); 251 event.preventDefault(); 252 } 253 254 this.ele.style.opacity = 0.5; 255 this.flag = true; 256 } 257 }; 258 this.move = function (event) { 259 260 if (this.flag) { 261 var minWidth = 10; 262 var left = Math.max(minWidth, event.clientX); 263 this.objLeft.style.width = left + "px"; 264 this.objRight.style.left = left + "px"; 265 this.ele.style.left = left - 2 + "px"; 266 if (!this.ele.releaseCapture) { 267 event.preventDefault(); 268 } 269 } 270 } 271 var t = this; 272 this.ele.onmousedown = function () { 273 t.down(event); 274 } 275 276 this.ele.onmouseup = function () { 277 t.up(event); 278 } 279 280 this.ele.onmousemove = function () { 281 t.move(event); 282 } 283 return this; 284 } 285 286 new Spliter(document.getElementById("left-panel"), document.getElementById("right-panel")); 287 </script> 288 </body> 289 290 </html> 291 </xsl:template> 292 <xsl:template name="summary"> 293 <div class="group">Summary</div> 294 <table align="center" class="details" border="0" cellpadding="5" cellspacing="2" width="95%"> 295 <tr valign="top"> 296 <th>请求总数</th> 297 <th>异常数</th> 298 <th>成功率</th> 299 <th>平均消耗时间</th> 300 <th>最小消耗时间</th> 301 <th>最大消耗时间</th> 302 </tr> 303 <tr valign="top"> 304 <xsl:variable name="allCount" select="count(/testResults/*)" /> 305 <xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s=\'false\'])" /> 306 <xsl:variable name="allSuccessCount" select="count(/testResults/*[attribute::s=\'true\'])" /> 307 <xsl:variable name="allSuccessPercent" select="$allSuccessCount div $allCount" /> 308 <xsl:variable name="allTotalTime" select="sum(/testResults/*/@t)" /> 309 <xsl:variable name="allAverageTime" select="$allTotalTime div $allCount" /> 310 <xsl:variable name="allMinTime"> 311 <xsl:call-template name="min"> 312 <xsl:with-param name="nodes" select="/testResults/*/@t" /> 313 </xsl:call-template> 314 </xsl:variable> 315 <xsl:variable name="allMaxTime"> 316 <xsl:call-template name="max"> 317 <xsl:with-param name="nodes" select="/testResults/*/@t" /> 318 </xsl:call-template> 319 </xsl:variable> 320 <xsl:attribute name="class"> 321 <xsl:choose> 322 <xsl:when test="$allFailureCount > 0">Failure</xsl:when> 323 </xsl:choose> 324 </xsl:attribute> 325 <td align="center"> 326 <xsl:value-of select="$allCount" /> 327 </td> 328 <xsl:if test="$allFailureCount > 0"> 329 <td align="center" style="color:red"> 330 <xsl:value-of select="$allFailureCount" /> 331 </td> 332 </xsl:if> 333 <xsl:if test="0 >= $allFailureCount"> 334 <td align="center"> 335 <xsl:value-of select="$allFailureCount" /> 336 </td> 337 </xsl:if> 338 <td align="center"> 339 <xsl:call-template name="display-percent"> 340 <xsl:with-param name="value" select="$allSuccessPercent" /> 341 </xsl:call-template> 342 </td> 343 <td align="center"> 344 <xsl:call-template name="display-time"> 345 <xsl:with-param name="value" select="$allAverageTime" /> 346 </xsl:call-template> 347 </td> 348 <td align="center"> 349 <xsl:call-template name="display-time"> 350 <xsl:with-param name="value" select="$allMinTime" /> 351 </xsl:call-template> 352 </td> 353 <td align="center"> 354 <xsl:call-template name="display-time"> 355 <xsl:with-param name="value" select="$allMaxTime" /> 356 </xsl:call-template> 357 </td> 358 </tr> 359 </table> 360 </xsl:template> 361 362 <xsl:template name="pagelist"> 363 <div class="group">Pages</div> 364 <table align="center" class="details" border="0" cellpadding="5" cellspacing="2" width="95%"> 365 <tr valign="top"> 366 <th width="50%" >请求内容</th> 367 <th>请求数</th> 368 <th>异常数</th> 369 <th>成功率</th> 370 <th>平均消耗时间</th> 371 <th>最小消耗时间</th> 372 <th>最大消耗时间</th> 373 </tr> 374 <xsl:for-each select="/testResults/*[not(@lb = preceding::*/@lb)]"> 375 <xsl:variable name="label" select="@lb" /> 376 <xsl:variable name="count" select="count(../*[@lb = current()/@lb])" /> 377 <xsl:variable name="failureCount" select="count(../*[@lb = current()/@lb][attribute::s=\'false\'])" /> 378 <xsl:variable name="successCount" select="count(../*[@lb = current()/@lb][attribute::s=\'true\'])" /> 379 <xsl:variable name="successPercent" select="$successCount div $count" /> 380 <xsl:variable name="totalTime" select="sum(../*[@lb = current()/@lb]/@t)" /> 381 <xsl:variable name="averageTime" select="$totalTime div $count" /> 382 <xsl:variable name="minTime"> 383 <xsl:call-template name="min"> 384 <xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" /> 385 </xsl:call-template> 386 </xsl:variable> 387 <xsl:variable name="maxTime"> 388 <xsl:call-template name="max"> 389 <xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" /> 390 </xsl:call-template> 391 </xsl:variable> 392 <tr valign="top"> 393 <xsl:attribute name="class"> 394 <xsl:choose> 395 <xsl:when test="$failureCount > 0">Failure</xsl:when> 396 </xsl:choose> 397 </xsl:attribute> 398 <td class="data" > 399 <xsl:value-of select="$label" /> 400 </td> 401 <td align="center"> 402 <xsl:value-of select="$count" /> 403 </td> 404 <xsl:if test="$failureCount > 0"> 405 <td align="center" style="color:red"> 406 <xsl:value-of select="$failureCount" /> 407 </td> 408 </xsl:if> 409 <xsl:if test="0 >= $failureCount"> 410 <td align="center"> 411 <xsl:value-of select="$failureCount" /> 412 </td> 413 </xsl:if> 414 <td align="center"> 415 <xsl:call-template name="display-percent"> 416 <xsl:with-param name="value" select="$successPercent" /> 417 </xsl:call-template> 418 </td> 419 <td align="center"> 420 <xsl:call-template name="display-time"> 421 <xsl:with-param name="value" select="$averageTime" /> 422 </xsl:call-template> 423 </td> 424 <td align="center"> 425 <xsl:call-template name="display-time"> 426 <xsl:with-param name="value" select="$minTime" /> 427 </xsl:call-template> 428 </td> 429 <td align="center"> 430 <xsl:call-template name="display-time"> 431 <xsl:with-param name="value" select="$maxTime" /> 432 </xsl:call-template> 433 </td> 434 </tr> 435 </xsl:for-each> 436 </table> 437 </xsl:template> 438 <xsl:template name="min"> 439 <xsl:param name="nodes" select="/.." /> 440 <xsl:choose> 441 <xsl:when test="not($nodes)">NaN</xsl:when> 442 <xsl:otherwise> 443 <xsl:for-each select="$nodes"> 444 <xsl:sort data-type="number" /> 445 <xsl:if test="position() = 1"> 446 <xsl:value-of select="number(.)" /> 447 </xsl:if> 448 </xsl:for-each> 449 </xsl:otherwise> 450 </xsl:choose> 451 </xsl:template> 452 453 <xsl:template name="max"> 454 <xsl:param name="nodes" select="/.." /> 455 <xsl:choose> 456 <xsl:when test="not($nodes)">NaN</xsl:when> 457 <xsl:otherwise> 458 <xsl:for-each select="$nodes"> 459 <xsl:sort data-type="number" order="descending" /> 460 <xsl:if test="position() = 1"> 461 <xsl:value-of select="number(.)" /> 462 </xsl:if> 463 </xsl:for-each> 464 </xsl:otherwise> 465 </xsl:choose> 466 </xsl:template> 467 468 <xsl:template name="display-percent"> 469 <xsl:param name="value" /> 470 <xsl:value-of select="format-number($value,\'0.00%\')" /> 471 </xsl:template> 472 473 <xsl:template name="display-time"> 474 <xsl:param name="value" /> 475 <xsl:value-of select="format-number($value,\'0 ms\')" /> 476 </xsl:template> 477 478 </xsl:stylesheet>
再次按上述方法执行ant命令,出来的报告就清晰明了啦,见下图: