一、背景

上一篇讲了Jmeter 接口自动化 - 脚本数据分离实例,我们知道怎么利用 Jmeter 去编写接口自动化脚本,但是接口自动化测试单有脚本是不够的,我们还需要批量跑指定接口,生成接口运行报告,定位报错接口,接口定时任务,邮件通知等功能。
批量跑指定接口:我们可以利用 ant 批量跑指定目录下的 Jmeter 脚本
生成接口运行报告:使用 Jmeter(extras 目录下) 自带的jmeter-results-detail-report_21.xsl样式文件
定位报错接口:使用扩展的jmeter-results-shanhe-me.xsl(可自行下载,我 git 地址也放了) 样式文件,输出的信息比自带的jmeter-results-detail-report_21.xsl
接口定时任务:使用 Jenkins 的 build periodically 完成
邮件通知:使用 Jenkins 的 Extended E-mail Notification 插件

顺序从 Jmeter-》Jmeter+ant-》Jmeter+ant+Jenkins 穿插原理,逐渐过渡,二中讲的是基础 (可跳过),三讲的是实践,遇到的问题以及扩展。

二、Jmeter+ant+Jenkins 接口自动化框架搭建

1 Jmeter

1、E:\sample 下有 sample.jmx 的脚本文件
2、追加 E:\apache-jmeter-2.13\bin 到 path 环境变量后
环境变量配置成功验证-》dos 中输入 jmeter,windows 会直接打开 jmeter
3、Dos 命令:
cd /d e:
cd sample
jmeter -n -t sample.jmx -l log.jtl

-n 这是指定 JMeter 在非用户界面模式运行
-t 包含测试计划的 JMX 文件的名字
-l 记录取样结果的 JTL 文件的名字

这里要讲两句,我们做接口自动化还好,要是用 Jmeter 做负载测试时,建议写好性能脚本后,用 NON GUI 模式进行负载测试,即非图形化界面,也就是建议使用命令行运行!因为图形化界面会消耗资源,导致负载测试结果不精确,特别是用图形化界面时还把查看结果树给打开,查看结果树输出的结果很多,所以,写完负载测试脚本后尽量把查看结果树等调试插件给禁掉
我们启动 Jmeter3.0 以上版本,也有建议不要使用图形化界面进行负载测试的提示:
翻译:不要使用 GUI 模式进行负载测试,只用于测试创建和测试调试!

2 Jmeter+ant

1、下载 ant 后在 window 中设置 ant 环境变量:
ANT_HOME 新建:E:\Program Files\apache-ant-1.7.1
path 追加:%ANT_HOME%\bin
环境变量配置成功验证-》dos 中输入 ant,提示:Buildfile: build.xml does not exist!

2、将 JMeter 所在目录下 extras 子目录里的 ant-JMeter-1.1.1.jar 复制到 Ant 所在目录 lib 子目录之下,这样 Ant 运行时才能找到"org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"这个类,从而成功触发 JMeter 脚本

3、jmeter 默认保存的是.csv 格式的文件,所以我们先要设置一下 bin/jmeter.properties 文件内容,保存
jmeter.save.saveservice.output_format=xml
4、主要逻辑思路:
E:\jmeter\build 下有 build_smoke_report.xml 文件
运行 E:\jmeter\script_smoke 下面的.jmx 脚本,生成.jtl 文件放入 E:\jmeter\report_smoke 文件中
然后 ant 再利用指定的.xsl 文件 (build_smoke_report.xml 定义) 去解析.jtl 文件,生成对应的 html 文件放入 E:\jmeter\report_smoke 中。
简写就是:跑完结果后,.xsl 文件把.jtl 文件转化成直观的 html 报告。

PS:XSL 是什么
XSL 是可扩展样式表语言(eXtensible Stylesheet Language)的外语缩写,是一种用于以可读格式呈现 XML(标准通用标记语言的子集)数据的语言。
易懂一点的说法:XSL 可描述如何来显示 XML 文档,XSL 之于 XML,就像 CSS 之于 HTML。

build_smoke_report.xml 文件中的内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project name="ant-jmeter-test" default="run" basedir=".">
      <tstamp>
        <format property="time" pattern="_yyyy_MMdd_HHmmss" />
    </tstamp>
    <!-- 需要改成自己本地的 Jmeter 目录-->  
    <property name="jmeter.home" value="E:\apache-jmeter-2.13" />
    <property name="report.title" value="接口测试"/>
    <!-- jmeter生成jtl格式的结果报告的路径--> 
    <property name="jmeter.result.jtl.dir" value="E:\jmeter\report_smoke" />
    <!-- jmeter生成html格式的结果报告的路径-->
    <property name="jmeter.result.html.dir" value="E:\jmeter\report_smoke" />
    <property name="detail" value="_detail" />
    <!-- 生成的报告的前缀-->  
    <property name="ReportName" value="SmokeReport" />
    <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" />

    <target name="run">
        <antcall target="test" />
        <antcall target="report" />
    </target>

    <target name="test">
        <taskdef name="jmeter" classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask" />
        <jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
            <!-- 声明要运行的脚本"*.jmx"指包含此目录下的所有jmeter脚本-->
            <testplans dir="E:\jmeter\script_smoke" 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>


    <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-detail-report_21.xsl">
              <param name="dateReport" expression="${report.datestamp}"/>
       </xslt>
                <!-- 因为上面生成报告的时候,不会将相关的图片也一起拷贝至目标目录,所以,需要手动拷贝 --> 
        <copy todir="${jmeter.result.html.dir}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
    </target>

</project>

Dos 切换到 E:\jmeter\build 目录下运行:ant –f build_smoke_report.xml
提示:BUILD SUCCESSFUL 代表 build_smoke_report.xml 文件运行成功
E:\jmeter\script_smoke 目录下会生成.jtl 和.html 文件

3 Jmeter+ant+Jenkins

1、 命令行切换到 jenkins.war 的根目录,输入 java -jar jenkins.war
2、 浏览器输入地址http://localhost:8080显示界面,安装成功

3、新建一个构建自由风格的软件项目并配置

4、设置定时任务,这里是设置每周一到周五的 8 点 30 分运行次 JOB。
定时构建语法:

第一颗*表示分钟,取值 0~59
第二颗*表示小时,取值 0~23
第三颗*表示一个月的第几天,取值 1~31
第四颗*表示第几月,取值 1~12
第五颗*表示一周中的第几天,取值 0~7,其中 0 和 7 代表的都是周日

4、构建->增加构建步骤->Invoke Ant->高级 (需要去 Jenkins 的系统配置->Global Tool Configuration 配置 ant 的地址),Build File 中输入 E:\jmeter\build\build_smoke_report.xml,保存

5、立即构建,运行成功,查看 console output 控制台输入的日志
出现 BUILD SUCCESSFUL 表示编译成功,跟 dos 运行 ant –f build_smoke_report.xml 效果一样

三、扩展的分割线

1·执行多个目录的 jmx 脚本


<jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
    <!-- 声明要运行的脚本"*.jmx"指包含此目录下的所有jmeter脚本-->
    <testplans dir="E:\jmeter\script_smoke" includes="*.jmx" />

    <property name="jmeter.save.saveservice.output_format" value="xml"/>
</jmeter>

改成


<jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
    <!-- 声明要运行的脚本"*.jmx"指包含此目录下的所有jmeter脚本-->
    <testplans dir="E:\jmeter\script_smoke" includes="*.jmx" />
    <testplans dir="E:\jmeter\script_smoke_two" includes="*.jmx" />

    <property name="jmeter.save.saveservice.output_format" value="xml"/>
</jmeter>

2.生成多个 html 报告

我这里是生成两个,一个简单的汇总报告,一个详细的报告 (用于定位接口报错)
build_smoke_report.xml 文件只利用 jmeter-results-detail-report_21.xsl 生成了一个 html 报告,想利用 jmeter-results-shanhe-me.xsl 生成第二个
添加属性值


<property name="detail" value="_detail" />
<property name="jmeter.result.jtlNamedetail" value="${jmeter.result.jtl.dir}/${ReportName}${time}.jtl" />
<property name="jmeter.result.htmlNamedetail" value="${jmeter.result.html.dir}/${ReportName}${time}${detail}.html" />

并且将


<target name="report">
    <tstamp> <format property="report.datestamp" pattern="yyyy/MM/dd HH:mm" /></tstamp>
   <xslt 
          classpathref="xslt.classpath"
          force="true"
          in="${jmeter.result.jtlNamedetail}"
          out="${jmeter.result.htmlNamedetail}"
          style="${jmeter.home}/extras/jmeter-results-shanhe-me.xsl">
          <param name="dateReport" expression="${report.datestamp}"/>
   </xslt>

改为


<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-detail-report_21.xsl">
          <param name="dateReport" expression="${report.datestamp}"/>
   </xslt>
   <xslt 
          classpathref="xslt.classpath"
          force="true"
          in="${jmeter.result.jtlNamedetail}"
          out="${jmeter.result.htmlNamedetail}"
          style="${jmeter.home}/extras/jmeter-results-shanhe-me.xsl">
          <param name="dateReport" expression="${report.datestamp}"/>
   </xslt>

如果知道 XML 的一些语法,应该一下就能看明白,我们只是加了一个标签,然后把里面用到的属性值定义好。好吧,不懂 XML 语法也能看懂,哈哈。

PS:
使用 jmeter-results-shanhe-me.xsl 的一些前置操作
1、下载 style 文件:jmeter.results.shanhe.me.xsl
2、把下载的文件放到 jmeter 的 extras 目录下。
3、修改 jmeter.properties 文件如下部分,我这里都修改成 true,这样执行完脚本后就会保存这些结果到.jtl 文件里面,重启 jmeter 生效:

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=false
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
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=false
jmeter.save.saveservice.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

3.XSL 文件的定制

其实 XSL 语法跟 XML 挺像的,多看看我们可以进行定制,如定义接口排序,扩展性能指标,那样我们性能测试也能利用这个生成报告,响应时间超过 3 秒的接口标黄显示,直观显示接口响应的性能状态,主要还是看公司的业务需求。

我会把扩展后的 XSL 也放到 git 地址,有需要的可以参考。

4.与 Jenkins 的集成

我们会用到两个插件,可到 Jenkins 的系统管理-》管理插件-》可选插件中搜索安装

4.1 HTML Publisher plugin

安装好 HTML Publisher plugin 之后,会在新建或者编辑项目时,在【增加构建后操作步骤】出现【Publish HTML reports】的选项

这样我们就可以在 Jenkins 中进行接口自动化测试报告的查看

4.2 Performance plugin

安装好 Performance plugin 之后,会在新建或者编辑项目时,在【增加构建后操作步骤】出现【Publish Performance test result report】的选项


仔细的朋友可以发现,我们使用 HTML Publisher plugin 用到了 html 文件,并且目录是指定的,难道每次每次生成的接口测试结果都要被覆盖?不存在的,当一个接口变动时,可通过接口历史响应,判断接口性能变化。使用 HTML Publisher plugin 用到了 jtl 文件名字是指定的,难道也要覆盖?也不存在的,这里放一下我的解决方案。

timestamp=`date +%Y%m%d%H%M%S`
pwd
rm -f ./*.html
mkdir  /jmeter/report/$timestamp
cp /jmeter/report/*.jtl /jmeter/report/$timestamp/
cp /jmeter/report/*.html /jmeter/report/$timestamp/

cp /jmeter/report/R*.html ./
rm -f /jmeter/report/latest/*.jtl
rm -f /jmeter/report/latest/*.html
cp /jmeter/report/*.jtl /jmeter/report/latest/
cd /jmeter/report/latest
mv -f *.jtl latest.jtl
rm -f /jmeter/report/*.jtl
mv -f /jmeter/report/*.html /jmeter/report/latest

思路:每次生成的两个 html 文件和 jtl 文件都放到类似 20170831102019(时间戳) 的目录下,保证每次的接口测试结果都能保存。
结果保存了,但是每次的 jtl 文件名字是不一样的,就算把 jtl 文件名字改成一致的,目录也是不同的,于是我们新建了一个 latest 目录,每次执行接口测试都保证 latest 目录的 html 和 jtl 文件是最新的,并且把 jtl 文件的名字改成 latest.jtl,完美达到我们的要求。只要思想不滑坡,办法总比困难多😋

4.3 Jenkins 不显示 html 的 CSS 样式

说到这里,会有一个问题,就是 Jenkins 默认不显示 html 的 CSS 样式,所以界面很丑,如图:


网上一致的解决方法:jenkins->系统管理->脚本命令行,执行命令:

System.setProperty("hudson.model.DirectoryBrowserSupport.CSP","sandbox allow-scripts; default-src 'none';script-src 'unsafe-inline' http://code.jquery.com/jquery-2.1.0.min.js; img-src dohko.hpeswlab.net 'self' data: ; style-src 'unsafe-inline' 'self';");
或  System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")

重新运行下 job 生成 html,就会正常显示。

但是,每次重启 Jenkins,都需要再运行一次,咋搞,不太智能。
我们理解一下网上解决方法的原理,就是执行一段 Groovy script,并且要在 JOB 开始前执行,然后我们知道有个 Groovy plugin,可以执行 Groovy 脚本,那么,我们,就。。。搞起来

html 报告显示正常的结果:

4.4 jenkins 配置自动发送邮件

1.开通 163 的 SMTP 服务,需要发一条短信,163 会给让你设置一个密码(不是你的 163 邮箱密码哦)
2.安装 Email Extension Plugin 插件
3.进入系统管理 -- 系统设置
注意自带邮箱插件->邮件通知 对应插件 E-mail Notification 的配置
扩展插件->Extended E-mail Notification 对应插件 Editable Email Notification 的配置
系统设置中邮件通知和 Extended E-mail Notification(配置项多一些) 的配置基本一致


JOB 页面使用 Extended E-mail Notification 插件的配置图:

按需整理好后会把用到的 build.xml,扩展的 xsl 和 Jenkins 中 JOB 的 config.xml 放到 git 地址
https://github.com/grizz/jmeter-master

欢迎交流指正,感谢阅读。


↙↙↙阅读原文可查看相关链接,并与作者交流