平臺(tái)簡(jiǎn)介
一個(gè)完整的接口自動(dòng)化測(cè)試平臺(tái)需要支持接口的自動(dòng)執(zhí)行,自動(dòng)生成測(cè)試報(bào)告,以及持續(xù)集成。Jmeter支持接口的測(cè)試,Ant支持自動(dòng)構(gòu)建,而Jenkins支持持續(xù)集成,所以三者組合在一起可以構(gòu)成一個(gè)功能完善的接口自動(dòng)化測(cè)試平臺(tái)。
搭建流程參考我之前一篇文章
Jmeter+Ant+Jenkins接口自動(dòng)化測(cè)試平臺(tái)搭建
SummaryReport優(yōu)化
Jmeter默認(rèn)生成報(bào)告不是很詳細(xì),因此我們需要進(jìn)行優(yōu)化。這里我們使用新的報(bào)告模板:jmeter-results-detail-report_30.xsl 默認(rèn)的報(bào)告模板是jmeter-results-detail-report_21
1.打開(kāi) build.xml 將21改為30
2.使用新的樣式表shanhe.xsl,放在jmeter\extras下,build文件新增將jtl轉(zhuǎn)化為html的語(yǔ)句(查看jmeter版本是否自帶,沒(méi)有新建一個(gè))
<condition property="style_version"value="_30">
補(bǔ)充刪除 .jtl的代碼,如果不刪除每次迭代的記錄都會(huì)顯示在報(bào)告里面,顯得很冗余。
<target name="run">
<echo>funcMode = ${funcMode}</echo>
<delete file="${testpath}/${test}.html"/>
<delete file="${testpath}/${test}.jtl"/>
進(jìn)入Jmeter的bin目錄,找到j(luò)meter.properties文件打開(kāi)。然后將以下代碼取消注釋狀態(tài),并改為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
最后執(zhí)行即可生成最新的報(bào)告:樣式如下,可以清晰看到每一個(gè)請(qǐng)求發(fā)送,響應(yīng)內(nèi)容。

注意
如果生成的報(bào)告為空,則說(shuō)明jmeter腳本有錯(cuò)誤,此時(shí)可以打開(kāi)jmeter的bin目錄中的jmeter.log日志文件,查看具體的報(bào)錯(cuò)信息。
進(jìn)一步優(yōu)化
打開(kāi)jmeter.results.shanhe.me.xsl文件定位該元素的類在該類的樣式下增加自動(dòng)換行的屬性
#right-panel .data { line-height: 19px; white-space: initial; }
運(yùn)行一下,斷言結(jié)果可以自動(dòng)換行了。其他部分換行按照同個(gè)方法即可

上圖可以看到,斷言輸出的換行"\n"被編譯成一個(gè)空格,把斷言結(jié)果用pre標(biāo)簽包起來(lái)
運(yùn)行,斷言結(jié)果換行符起作用了看起來(lái)差不多了(∩_∩)~

DetailReport優(yōu)化問(wèn)題
這個(gè)問(wèn)題沒(méi)有遇到,僅作為記錄,有遇到的小伙伴歡迎留言文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-692087.html
測(cè)試報(bào)告為空,MinTime和MaxTime為NaN文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-692087.html
1.樣式表版本不對(duì),所以網(wǎng)上找了到j(luò)meter-results-detail-report_21.xsl,下載放到j(luò)meter->extra下
2.把jmeter->lib下的xalan.jar、serializer.jar復(fù)制到ant->lib下
附使用的三個(gè)文件如下
build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="ant-jmeter-test" default="run" basedir=".">
<property environment="env"/>
<tstamp>
<format property="time" pattern="yyyy_MM_dd_hh_mm" />
</tstamp>
<!-- 需要調(diào)用的jmeter目錄,根據(jù)需要進(jìn)行修改,本次使用的linux路徑-->
<property name="jmeter.home" value="D:\apache-jmeter-5.2.1\apache-jmeter-5.4.1" />
<property name="report.title" value="接口自動(dòng)化測(cè)試"/>
<!-- 報(bào)告匯總-->
<property name="jmeter.result.all.dir" value="D:\apache-jmeter-5.2.1\workspace\result"/>
<!-- jmeter生成jtl格式的結(jié)果報(bào)告的路徑-->
<property name="jmeter.result.jtl.dir" value="${jmeter.result.all.dir}\jtl" />
<!-- jmeter生成html格式的結(jié)果報(bào)告的路徑-->
<property name="jmeter.result.html.dir" value="${jmeter.result.all.dir}\html" />
<!-- 【詳細(xì)報(bào)告】jmeter生成html格式的詳細(xì)報(bào)告的路徑-->
<property name="jmeter.result.html.dir1" value="${jmeter.result.all.dir}\report" />
<!-- 生成的報(bào)告的前綴-->
<property name="ReportName" value="接口自動(dòng)化匯總報(bào)告_" />
<property name="ReportName1" value="接口自動(dòng)化詳細(xì)報(bào)告_" />
<property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/${ReportName}${time}.jtl" />
<property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}${time}.html" />
<!-- 【詳細(xì)報(bào)告】詳細(xì)報(bào)告的文件名-->
<property name="jmeter.result.htmlName1" value="${jmeter.result.html.dir1}/${ReportName1}${time}.html" />
<target name="run">
<antcall target="clear" />
<antcall target="test" />
<antcall target="report" />
<antcall target="zip_all"/>
</target>
<target name="zip_all" >
<zip destfile="${jmeter.result.all.dir}.zip" basedir="${jmeter.result.all.dir}">
</zip>
</target>
<target name="clear">
<!-- 每次執(zhí)行前先刪除清空jtl和html文件夾,釋放空間 -->
<echo message="釋放空間,清空jtl和html文件夾中 ..."></echo>
<delete dir="${jmeter.result.jtl.dir}" />
<delete dir="${jmeter.result.html.dir}" />
<delete dir="${jmeter.result.html.dir1}"/>
<!-- 刪除單個(gè)文件-->
<delete file="${jmeter.result.all.dir}.zip" />
<sleep seconds="2"/>
<echo message="釋放完成 ..."></echo>
</target>
<!-- 該命令為執(zhí)行命令-->
<target name="test">
<taskdef name="jmeter" classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask" />
<jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
<!-- 聲明要運(yùn)行的腳本路徑"*.jmx"指包含此目錄下的所有jmeter腳本-->
<testplans dir="D:\apache-jmeter-5.2.1\workspace" includes="*.jmx" />
<property name="jmeter.save.saveservice.output_format" value="xml"/>
</jmeter>
</target>
<path id="xslt.classpath">
<fileset dir="${jmeter.home}/lib" includes="xalan*.jar"/>
<fileset dir="${jmeter.home}/lib" includes="serializer*.jar"/>
</path>
<!-- 該命令為生成匯總和詳細(xì)報(bào)告 -->
<target name="report">
<tstamp> <format property="report.datestamp" pattern="yyyy/MM/dd HH:mm" /></tstamp>
<xslt
classpathref="xslt.classpath"
force="true"
in="${jmeter.result.jtlName}"
out="${jmeter.result.htmlName}"
style="${jmeter.home}/extras/jmeter-results-report_21.xsl">
<param name="dateReport" expression="${report.datestamp}"/>
</xslt>
<!-- 【詳細(xì)報(bào)告】指定詳細(xì)報(bào)告模板文件-->
<xslt
classpathref="xslt.classpath"
force="true"
in="${jmeter.result.jtlName}"
out="${jmeter.result.htmlName1}"
style="${jmeter.home}/extras/jmeter.results.shanhe.me.xsl">
<param name="dateReport" expression="${report.datestamp}"/>
</xslt>
<!-- 因?yàn)樯厦嫔蓤?bào)告的時(shí)候,不會(huì)將相關(guān)的圖片也一起拷貝至目標(biāo)目錄,所以,需要手動(dòng)拷貝 -->
<copy todir="${jmeter.result.html.dir}">
<fileset dir="${jmeter.home}/extras">
<include name="collapse.png" />
<include name="expand.png" />
</fileset>
</copy>
<!-- 【詳細(xì)報(bào)告】拷貝圖片到目標(biāo)目錄-->
<copy todir="${jmeter.result.html.dir1}">
<fileset dir="${jmeter.home}/extras">
<include name="collapse.png" />
<include name="expand.png" />
</fileset>
</copy>
</target>
</project>
jmeter-results-detail-report_30.xsl
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!--
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.
-->
<!--
Stylesheet for processing 3.0 output format test result files
To uses this directly in a browser, add the following to the JTL file as line 2:
<?xml-stylesheet type="text/xsl" href="../extras/jmeter-results-detail-report_30.xsl"?>
and you can then view the JTL in a browser.Edit by ZHC
-->
<xsl:output method="html" indent="yes" encoding="UTF-8" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
<!-- Defined parameters (overrideable) -->
<xsl:param name="showData" select="'y'"/>
<xsl:param name="titleReport" select="'Interface Test Results'"/>
<xsl:param name="dateReport" select="'date not defined'"/>
<xsl:template match="testResults">
<html>
<head>
<title><xsl:value-of select="$titleReport" /></title>
<style type="text/css">
body {
font:normal 68% verdana,arial,helvetica;
color:#000000;
}
table tr td, table tr th {
font-size: 68%;
}
table.details tr th{
color: #ffffff;
font-weight: bold;
text-align:center;
background:#2674a6;
white-space: nowrap;
}
table.details tr td{
background:#eeeee0;
white-space: nowrap;
}
h1 {
margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
}
h2 {
margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
}
h3 {
margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
}
.Failure {
font-weight:bold; color:red;
}
.LongTime {
font-weight:bold; color:#ff9900;
}
img
{
border-width: 0px;
}
.expand_link
{
position=absolute;
right: 0px;
width: 27px;
top: 1px;
height: 27px;
}
.page_details
{
display: none;
}
.page_details_expanded
{
display: block;
display/* hide this definition from IE5/6 */: table-row;
}
</style>
<script language="JavaScript"><![CDATA[
function expand(details_id)
{
document.getElementById(details_id).className = "page_details_expanded";
}
function collapse(details_id)
{
document.getElementById(details_id).className = "page_details";
}
function change(details_id)
{
var _dataType=document.getElementById(details_id+"_image").getAttribute('data-type');
if(_dataType=='expand')
{
<!-- document.getElementById(details_id+"_image").src = "collapse.png"; -->
document.getElementById(details_id+"_image").src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAGUExURSZ0pv///xB+eSAAAAARSURBVAjXY2DABuR/gBA2AAAzpwIvNQARCgAAAABJRU5ErkJggg==";
expand(details_id);
document.getElementById(details_id+"_image").setAttribute('data-type','collapse');
}
else
{
<!-- document.getElementById(details_id+"_image").src = "expand.png"; -->
document.getElementById(details_id+"_image").src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAGUExURSZ0pv///xB+eSAAAAAWSURBVAjXY2CAAcYGBJL/AULIIjAAAJJrBjcL30J5AAAAAElFTkSuQmCC";
collapse(details_id);
document.getElementById(details_id+"_image").setAttribute('data-type','expand');
}
}
]]></script>
</head>
<body>
<xsl:call-template name="pageHeader" />
<xsl:call-template name="summary" />
<hr size="1" width="95%" align="center" />
<xsl:call-template name="pagelist"/>
<hr size="1" width="95%" align="center" />
<xsl:call-template name="detail" />
</body>
</html>
</xsl:template>
<xsl:template name="pageHeader">
<h1><xsl:value-of select="$titleReport" /></h1>
<table width="100%">
<tr>
<!-- 獲取requestHeader數(shù)據(jù) -->
<xsl:variable name="req" select="/testResults/httpSample/requestHeader" />
<!-- 從獲取的URL中截取數(shù)據(jù),以Host:分割,取后半部分 -->
<xsl:variable name="remaining" select="substring-after($req,'Host:')" />
<!-- 已換行符來(lái)截取,before代表?yè)Q行符之前的數(shù)據(jù) -->
<xsl:variable name="host" select="substring-before($remaining, '
')" />
<td align="left">Date report: <xsl:value-of select="$dateReport" /></td>
<td align="center">Host: <xsl:value-of select="$host" /></td>
<td align="right"><a href="./TestLog.html">測(cè)試日志</a></td>
</tr>
</table>
<hr size="1" />
</xsl:template>
<xsl:template name="summary">
<h2>Summary</h2>
<table align="center" class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<tr valign="top">
<th># Samples</th>
<th>Success</th>
<th>Failures</th>
<th>Success Rate</th>
<th>Average Time</th>
<th>Min Time</th>
<th>Max Time</th>
<th>90% Line</th>
<th>QPS</th>
</tr>
<tr valign="top">
<xsl:variable name="allCount" select="count(/testResults/*)" />
<xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s='false'])" />
<xsl:variable name="allSuccessCount" select="count(/testResults/*[attribute::s='true'])" />
<xsl:variable name="allSuccessPercent" select="$allSuccessCount div $allCount" />
<xsl:variable name="allTotalTime" select="sum(/testResults/*/@t)" />
<xsl:variable name="allAverageTime" select="$allTotalTime div $allCount" />
<xsl:variable name="allMinTime">
<xsl:call-template name="min">
<xsl:with-param name="nodes" select="/testResults/*/@t" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="allMaxTime">
<xsl:call-template name="max">
<xsl:with-param name="nodes" select="/testResults/*/@t" />
</xsl:call-template>
</xsl:variable>
<!-- New add 90% line -->
<xsl:variable name="allLineTime">
<xsl:call-template name="lineTime">
<xsl:with-param name="nodes" select="/testResults/*/@t" />
</xsl:call-template>
</xsl:variable>
<!-- 將毫秒轉(zhuǎn)換成秒 -->
<xsl:variable name="qps" select="$allCount * 1000 div $allTotalTime"/>
<!--
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$allFailureCount > 0">Failure</xsl:when>
</xsl:choose>
</xsl:attribute>
-->
<td align="center">
<xsl:value-of select="$allCount" />
</td>
<td align="center">
<xsl:value-of select="$allSuccessCount" />
</td>
<xsl:choose>
<xsl:when test="$allFailureCount > 0">
<td align="center" style="font-weight:bold">
<font color="red">
<xsl:value-of select="$allFailureCount" />
</font>
</td>
</xsl:when>
<xsl:otherwise>
<td align="center">
<xsl:value-of select="$allFailureCount" />
</td>
</xsl:otherwise>
</xsl:choose>
<td align="center">
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="$allSuccessPercent" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allAverageTime" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allMinTime" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allMaxTime" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allLineTime" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-qps">
<xsl:with-param name="value" select="$qps" />
</xsl:call-template>
</td>
</tr>
</table>
</xsl:template>
<xsl:template name="pagelist">
<h2>Pages</h2>
<table align="center" class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<tr valign="top">
<th>URL</th>
<th># Samples</th>
<th>Success</th>
<th>Failures</th>
<th>Success Rate</th>
<th>Average Time</th>
<th>Min Time</th>
<th>Max Time</th>
<th>90% Line</th>
<th>QPS</th>
<th></th>
</tr>
<xsl:for-each select="/testResults/*[not(@lb = preceding::*/@lb)]" >
<!-- 按平均時(shí)間排序 -->
<xsl:sort select="sum(../*[@lb = current()/@lb]/@t) div count(../*[@lb = current()/@lb])" data-type="number" order="descending"/>
<xsl:variable name="label" select="@lb" />
<xsl:variable name="count" select="count(../*[@lb = current()/@lb])" />
<xsl:variable name="failureCount" select="count(../*[@lb = current()/@lb][attribute::s='false'])" />
<xsl:variable name="successCount" select="count(../*[@lb = current()/@lb][attribute::s='true'])" />
<xsl:variable name="successPercent" select="$successCount div $count" />
<xsl:variable name="totalTime" select="sum(../*[@lb = current()/@lb]/@t)" />
<xsl:variable name="averageTime" select="$totalTime div $count" />
<xsl:variable name="minTime">
<xsl:call-template name="min">
<xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="maxTime">
<xsl:call-template name="max">
<xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" />
</xsl:call-template>
</xsl:variable>
<!-- new add 90% line time -->
<xsl:variable name="nintyTime">
<xsl:call-template name="lineTime">
<xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="qpsTime" select="$count * 1000 div $totalTime"/>
<tr valign="top">
<xsl:choose>
<!-- 失敗用例標(biāo)紅顯示 -->
<xsl:when test="$failureCount > 0">
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$failureCount > 0">Failure</xsl:when>
</xsl:choose>
</xsl:attribute>
</xsl:when>
<!-- 平均時(shí)間超過(guò)2s,標(biāo)色顯示 -->
<xsl:when test="$averageTime > 2000">
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$averageTime > 2000">LongTime</xsl:when>
</xsl:choose>
</xsl:attribute>
</xsl:when>
</xsl:choose>
<td align="center">
<xsl:if test="$failureCount > 0">
<a><xsl:attribute name="href">#<xsl:value-of select="$label" /></xsl:attribute>
<xsl:value-of select="$label" />
</a>
</xsl:if>
<xsl:if test="0 >= $failureCount">
<xsl:value-of select="$label" />
</xsl:if>
</td>
<td align="center">
<xsl:value-of select="$count" />
</td>
<td align="center">
<xsl:value-of select="$successCount" />
</td>
<td align="center">
<xsl:value-of select="$failureCount" />
</td>
<td align="center">
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="$successPercent" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$averageTime" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$minTime" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$maxTime" />
</xsl:call-template>
</td>
<!-- Page頁(yè)面添加90% LineTime -->
<td align="center">
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$nintyTime" />
</xsl:call-template>
</td>
<td align="center">
<xsl:call-template name="display-qps">
<xsl:with-param name="value" select="$qpsTime" />
</xsl:call-template>
</td>
<td align="center">
<a href="javascript:void(0)">
<xsl:attribute name="href"><xsl:text/>javascript:change('page_details_<xsl:value-of select="position()" />')</xsl:attribute>
<!-- <img src="expand.png" alt="expand/collapse"><xsl:attribute name="id"><xsl:text/>page_details_<xsl:value-of select="position()" />_image</xsl:attribute></img> -->
<img data-type="expand" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAGUExURSZ0pv///xB+eSAAAAAWSURBVAjXY2CAAcYGBJL/AULIIjAAAJJrBjcL30J5AAAAAElFTkSuQmCC" alt="expand/collapse"><xsl:attribute name="id"><xsl:text/>page_details_<xsl:value-of select="position()" />_image</xsl:attribute></img>
</a>
</td>
</tr>
<tr class="page_details">
<xsl:attribute name="id"><xsl:text/>page_details_<xsl:value-of select="position()" /></xsl:attribute>
<td colspan="8" bgcolor="#FF0000">
<div align="center">
<b>Details for Page "<xsl:value-of select="$label" />"</b>
<table bordercolor="#000000" bgcolor="#2674A6" border="0" cellpadding="1" cellspacing="1" width="95%">
<tr>
<th>Thread</th>
<th>Iteration</th>
<th>Time (milliseconds)</th>
<th>Bytes</th>
<th>Success</th>
</tr>
<xsl:for-each select="../*[@lb = $label and @tn != $label]">
<tr>
<td align="center"><xsl:value-of select="@tn" /></td>
<td align="center"><xsl:value-of select="position()" /></td>
<td align="center"><xsl:value-of select="@t" /></td>
<!-- TODO allow for missing bytes field -->
<td align="center"><xsl:value-of select="@by" /></td>
<td align="center"><xsl:value-of select="@s" /></td>
</tr>
</xsl:for-each>
</table>
</div>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="detail">
<xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s='false'])" />
<xsl:if test="$allFailureCount > 0">
<h2>Failure Detail</h2>
<xsl:for-each select="/testResults/*[not(@lb = preceding::*/@lb)]">
<xsl:variable name="failureCount" select="count(../*[@lb = current()/@lb][attribute::s='false'])" />
<xsl:if test="$failureCount > 0">
<h3><xsl:value-of select="@lb" /><a><xsl:attribute name="name"><xsl:value-of select="@lb" /></xsl:attribute></a></h3>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<tr valign="top">
<th align="center">Response</th>
<th align="center">Failure Message</th>
<xsl:if test="$showData = 'y'">
<th align="left">Response Data</th>
</xsl:if>
</tr>
<xsl:for-each select="/testResults/*[@lb = current()/@lb][attribute::s='false']">
<tr>
<td><xsl:value-of select="@rc | @rs" /> - <xsl:value-of select="@rm" /></td>
<td><xsl:value-of select="assertionResult/failureMessage" /></td>
<xsl:if test="$showData = 'y'">
<td><xsl:value-of select="responseData" /></td>
</xsl:if>
</tr>
</xsl:for-each>
</table>
</xsl:if>
</xsl:for-each>
</xsl:if>
</xsl:template>
<xsl:template name="min">
<xsl:param name="nodes" select="/.." />
<xsl:choose>
<xsl:when test="not($nodes)">NaN</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$nodes">
<xsl:sort data-type="number" />
<xsl:if test="position() = 1">
<xsl:value-of select="number(.)" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="max">
<xsl:param name="nodes" select="/.." />
<xsl:choose>
<xsl:when test="not($nodes)">NaN</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$nodes">
<xsl:sort data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="number(.)" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- 90% line time -->
<xsl:template name="lineTime">
<xsl:param name="nodes" select="/.." />
<xsl:choose>
<xsl:when test="not($nodes)">NaN</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$nodes">
<xsl:sort data-type="number" />
<!-- last() 返回當(dāng)前上下文中的最后一個(gè)節(jié)點(diǎn)位置數(shù) -->
<!-- ceiling(number) 返回大于number的最小整數(shù) -->
<!-- floor(number) 返回不大于number的最大整數(shù) -->
<!-- position() 返回當(dāng)前節(jié)點(diǎn)位置的數(shù)字 -->
<!-- number(object) 使對(duì)象轉(zhuǎn)換成數(shù)字 -->
<xsl:choose>
<!-- 當(dāng)只有一個(gè)節(jié)點(diǎn)時(shí),向上取整 -->
<xsl:when test="last() = 1">
<xsl:if test="position() = ceiling(last()*0.9)">
<xsl:value-of select="number(.)" />
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="position() = floor(last()*0.9)">
<xsl:value-of select="number(.)" />
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="display-percent">
<xsl:param name="value" />
<xsl:value-of select="format-number($value,'0.00%')" />
</xsl:template>
<xsl:template name="display-time">
<xsl:param name="value" />
<xsl:value-of select="format-number($value,'0 ms')" />
</xsl:template>
<xsl:template name="display-qps">
<xsl:param name="value" />
<xsl:value-of select="format-number($value,'0.000 /s')" />
</xsl:template>
</xsl:stylesheet>
jmeter.results.shanhe.me.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<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"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/testResults">
<html lang="en">
<head>
<meta name="Author" content="shanhe.me"/>
<title>JMeter Test Results</title>
<style type="text/css"><![CDATA[
* { margin: 0; padding: 0 }
html, body { width: 100%; height: 100%; background: #b4b4b4; font-size: 12px }
table { border: none; border-collapse: collapse; table-layout: fixed }
td { vertical-align: baseline; font-size: 12px }
#left-panel { position: absolute; left: 0; top: 0; bottom: 0; width: 300px; overflow: auto; background: #dee4ea }
#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) }
#left-panel li.success { color: #565b60 }
#left-panel li.failure { color: red }
#left-panel li { list-style: none; color: black; cursor: pointer }
#left-panel li.selected { background-repeat: repeat-x; color: white; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAUCAYAAABMDlehAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBQxLTs5O2gAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAEdJREFUCNc1y7ERgEAMA0GNUhIyGqM2uqKgtyWZhE9v53A/7/A6D7BkMDNgy2AroB2wHTCZv5UMOgFLG1bvd7XBckBlwCXjA5wMOF5iOX/MAAAAAElFTkSuQmCC) }
#left-panel div { line-height: 20px; background-position: 25px 3px; background-repeat: no-repeat; padding: 0 0 0 45px }
#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=) }
#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) }
#left-panel div.detail { display: none }
#right-panel { position: absolute; right: 0; top: 0; bottom: 0; left: 301px; overflow: auto; background: white }
#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=) }
#right-panel .zebra { background-repeat: repeat; padding: 0 0 0 18px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAmCAYAAAAFvPEHAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBYWFlNztEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAABdJREFUCNdjYKAtePv5338mBgYGBpoQAGy1BAJlb/y6AAAAAElFTkSuQmCC) }
#right-panel .data { line-height: 19px; white-space: nowrap }
#right-panel pre.data { white-space: pre }
#right-panel tbody.failure { color: red }
#right-panel td.key { min-width: 108px }
#right-panel td.delimiter { min-width: 18px }
#right-panel td.assertion:before { counter-increment: assertion; content: counter(assertion) ". " }
#right-panel td.assertion { color: black }
#right-panel .trail { border-top: 1px solid #b4b4b4 }
]]></style>
<script type="text/javascript"><![CDATA[
var onclick_li = (function() {
var last_selected = null;
return function(li) {
if( last_selected == li )
return;
if( last_selected )
last_selected.className = "";
last_selected = li;
last_selected.className = "selected";
document.getElementById("right-panel").innerHTML = last_selected.firstChild.nextSibling.innerHTML;
return false;
};
})();
var patch_timestamp = function() {
var spans = document.getElementsByTagName("span");
var len = spans.length;
for( var i = 0; i < len; ++i ) {
var span = spans[i];
if( "patch_timestamp" == span.className )
span.innerHTML = new Date( parseInt( span.innerHTML ) );
}
};
var patch_navigation_class = (function() {
var set_class = function(el, flag) {
if(el) {
el.className += flag ? " success" : " failure";
}
};
var traverse = function(el, group_el, flag) {
while(1) {
if(el) {
if(el.className == 'navigation') {
set_class(group_el, flag);
group_el = el;
flag = true;
} else {
var o = el.firstChild;
o = o ? o.className : null;
flag = flag ? (o == 'success') : false;
}
el = el.nextSibling;
} else {
set_class(group_el, flag);
break;
}
}
};
return function() {
var o = document.getElementById("result-list");
o = o ? o.firstChild : null;
if(o)
traverse(o, null, true);
};
})();
window.onload = function() {
patch_timestamp();
patch_navigation_class();
var o = document.getElementById("result-list");
o = o ? o.firstChild : null;
o = o ? o.nextSibling : null;
if(o)
onclick_li(o);
};
]]></script>
</head>
<body>
<div id="left-panel">
<ol id="result-list">
<xsl:for-each select="*">
<!-- group with the previous sibling -->
<xsl:if test="position() = 1 or @tn != preceding-sibling::*[1]/@tn">
<li class="navigation">Thread: <xsl:value-of select="@tn"/></li>
</xsl:if>
<li onclick="return onclick_li(this);">
<div>
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="@s = 'true'">success</xsl:when>
<xsl:otherwise>failure</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:value-of select="@lb"/>
</div><div class="detail">
<div class="group">Sampler</div>
<div class="zebra">
<table>
<tr><td class="data key">Thread Name</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@tn"/></td></tr>
<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>
<tr><td class="data key">Time</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@t"/> ms</td></tr>
<tr><td class="data key">Latency</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@lt"/> ms</td></tr>
<tr><td class="data key">Bytes</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@by"/></td></tr>
<tr><td class="data key">Sample Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@sc"/></td></tr>
<tr><td class="data key">Error Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@ec"/></td></tr>
<tr><td class="data key">Response Code</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rc"/></td></tr>
<tr><td class="data key">Response Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rm"/></td></tr>
</table>
</div>
<div class="trail"></div>
<xsl:if test="count(assertionResult) > 0">
<div class="group">Assertion</div>
<div class="zebra">
<table>
<xsl:for-each select="assertionResult">
<tbody>
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="failure = 'true'">failure</xsl:when>
<xsl:when test="error = 'true'">failure</xsl:when>
</xsl:choose>
</xsl:attribute>
<tr><td class="data assertion" colspan="3"><xsl:value-of select="name"/></td></tr>
<tr><td class="data key">Failure</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failure"/></td></tr>
<tr><td class="data key">Error</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="error"/></td></tr>
<tr><td class="data key">Failure Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failureMessage"/></td></tr>
</tbody>
</xsl:for-each>
</table>
</div>
<div class="trail"></div>
</xsl:if>
<div class="group">Request</div>
<div class="zebra">
<table>
<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>
<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>
<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>
<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>
</table>
</div>
<div class="trail"></div>
<div class="group">Response</div>
<div class="zebra">
<table>
<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>
<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>
<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>
</table>
</div>
<div class="trail"></div>
</div>
</li>
</xsl:for-each>
</ol>
</div>
<div id="right-panel"></div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
到了這里,關(guān)于Jmeter+Ant+Jenkins自動(dòng)化搭建之報(bào)告優(yōu)化的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!