【精通JMeter接口测试】03-JMeter 接口测试持续集成踩坑记:jtl 转 Allure 报告、Jenkins 定时执行、CSP 样式劫持全解决

前言

老铁们,咱们用 JMeter 跑测试的时候,最直观的就是盯着"查看结果树"看结果对吧?等到接口一个个都调通了,后面就得面对一个现实------敏捷开发嘛,迭代是逃不掉的。每次迭代都得把接口测试再跑一遍,这重复的活儿干多了,脑子都快变复读机了......那咋整?当然得搞持续集成啦!那么问题来了:咱们具体该怎么把这事儿集成起来呢?------这就来聊聊本期的话题。

咱们依旧截图+步骤详细整合,看完一定有帮助

text 复制代码
✔️ 每天到点自动执行 JMeter 脚本
✔️ 生成带请求/响应详情的 Allure 报告
✔️ Jenkins 集成 HTML 报告且样式不丢失
✔️ 一套 build.xml 通用模板,改路径直接用

目录

一、精通Jmeter接口测试命令模式运行

1. 命令行的参数

-n-t 必须一起使用,不能单独使用

  • -n:表示命令行模式(非GUI)
  • -t:指定你的 JMeter 的 .jmx 脚本

-l 表示用来生成 jtl 报告

好比是一个中转站 ,通过 jtl 的报告可以衍生出其他格式,比如将 jtl 报告转换成 HTML 的报告

注意:-l 是可选的,如果你直接用 -e -o 生成 HTML 报告,可以不带 -l


-e-o 必须一起使用,不能单独使用

  • -e:表示生成 HTML 格式的报告
  • -o:指定报告的输出目录
    但是对于这个输出目录这个目录必须是一个空目录,如果里面有内容,那么它就会报错。

另外:

  1. -e -o 通常要和 -n -t(运行测试)或者 -g(已有 jtl 文件)配合使用。
  2. -e -o只支持csv格式,即jmeter.save.saveservice.output_format=csv(所以需要修改:bin/jmeter.propties文件,我们后面会讲怎么修改)

2. 使用示例及报错处理

1、首先你看,我们前面两篇博客所做的接口测试如下:👇


2、那么现在我先将Jmeter这个软件给关闭,然后呢,我们打开测试01.jmx这个脚本


3、然后:我们把它拷贝到如下所示的目录中去👇


4、此时我们打开我们的dos命令窗口,然后切换到我们刚才的E:\接口测试工具的使用\命令使用Jmeter目录里面去。


5、此时我们就开始使用命令jmeter -n -t 指定脚本.jmx -l jtl报告名.jtl

但是这种jtl报告我们是读不懂的,它好比就是一个中间的报告,我们得通过它转换成html报告,如下👇
jmeter -n -t 测试01.jmx -l result.jtl -e -o test

问题:如果你这里面的配置文件没有进行如下的设置,那么一定会报错的,就是说,



6、OK,最后我们来观察我们的html报告,如下👇

报告中的信息描述 如下👇:

1)

2)

3)

4)


上述我们是需要说比较麻烦的命令才得到我们的结果,那其实它有个比较简单的方法,就是我们可以集成Ant来为什么做上述的事情,如何集成呢?老铁,继续向后看~~


二、精通Jmeter接口测试集成Ant运行

你只需要一步一步的跟着我做如下步骤即可,👇

1、下载Ant压缩包并解压然后配置PATH变量

🚀JMeter 5.6.3 要求:Ant 版本 ≥ 1.10.1

1.1、下载Ant压缩包

Ant压缩包官网

1.2、解压缩


1.3、配置PATH变量

把ant的bin目录配置到PATH目录,如下👇


2、配置ant的编译文件build.xml

2.1、build.xml文件模版

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>
    <!-- 需要调用的jmeter目录,根据需要进行修改,本次使用的linux路径-->  
    <property name="jmeter.home" value="此处修改为你的JMeter安装目录" />
    <property name="report.title" value="接口自动化测试"/>
    <!-- jmeter生成jtl格式的结果报告的路径--> 
    <property name="jmeter.result.jtl.dir" value="此处修改为你的jtl报告存放目录" />
    <!-- jmeter生成html格式的结果报告的路径-->
    <property name="jmeter.result.html.dir" value="此处修改为你的html报告存放目录" />
	<!-- 【详细报告】jmeter生成html格式的详细报告的路径-->
	<property name="jmeter.result.html.dir1" value="report" />
    <!-- 生成的报告的前缀-->  
    <property name="ReportName" value="接口自动化汇总报告" />
	<property name="ReportName1" value="接口自动化详细报告" />
    <property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/${ReportName}.jtl" />
    <property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}.html" />
	<!-- 【详细报告】详细报告的文件名-->
    <property name="jmeter.result.htmlName1" value="${jmeter.result.html.dir1}/${ReportName1}.html" />
    <target name="run">
	   <!--antcall target="delete" /-->
        <antcall target="test" />
        <antcall target="report" />
    </target>
	
    <!-- 该命令用来删除今天已经执行过的jtl,防止旧数据重叠
	<target name ="delete">
		<delete file="${jmeter.result.jtl.dir}/${ReportName}${env.BUILD_ID}.jtl"/>
	</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="此处修改为你的jmx脚本存放目录" 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-report_21.xsl">
              <param name="dateReport" expression="${report.datestamp}"/>
       </xslt>
	   <!-- 【详细报告】指定详细报告模板文件-->
	   <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>
	 

                <!-- 因为上面生成报告的时候,不会将相关的图片也一起拷贝至目标目录,所以,需要手动拷贝 --> 
        <copy todir="${jmeter.result.html.dir}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
		<!-- 【详细报告】拷贝图片到目标目录-->
		<copy todir="${jmeter.result.html.dir1}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
    </target>
</project>

2.2、这个build需要改动的地方如下:

bash 复制代码
jmeter的目录:<property name="jmeter.home" value="此处修改为JMeter安装目录" />
jtl报告的目录:<property name="jmeter.result.jtl.dir" value="此处修改为jtl报告存放目录" />
html报告的目录:<property name="jmeter.result.html.dir" value="此处修改为html报告存放目录" />
jmx脚本的目录:<testplans dir="此处修改为jmx脚本存放目录" includes="*.jmx" />

2.3、简述原因

jmeter的目录:<property name="jmeter.home" value="此处修改为JMeter安装目录" />
原因:指定本地JMeter的根安装路径,Ant才能调用JMeter执行脚本,必须填写你电脑上真实的JMeter安装目录

jtl报告的目录:<property name="jmeter.result.jtl.dir" value="此处修改为jtl报告存放目录" />
原因:设置JMeter执行后生成的jtl结果文件的保存路径,需自定义本地文件夹路径

html报告的目录:<property name="jmeter.result.html.dir" value="此处修改为html报告存放目录" />
原因:设置JMeter测试后生成的HTML汇总报告的保存路径,需自定义本地文件夹路径

jmx脚本的目录:<testplans dir="此处修改为jmx脚本存放目录" includes="*.jmx" />
原因:指定需要执行的JMeter测试脚本(.jmx文件)所在的目录,Ant会读取该目录下所有脚本执行,必须填写脚本真实路径

2.4、build.xml的配置

OK,那么我们开始配置build.xml文件了:

1)首先为了防止我们的后续出问题,我们将Jmeter脚本测试01.jmx放到非根目录和非中文目录中

2)然后我们的build.xml 必须放在 JMeter 脚本(.jmx)所在的目录

因此我们在测试01.jmx所在目录中建一个build.xml,如下

3)然后我们打开这个配置文件并将上述中的build.xml的模版拷贝进去

4)修改之后的配置文件如下:


代码

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>
    <!-- 需要调用的jmeter目录,根据需要进行修改,本次使用的linux路径-->  
    <property name="jmeter.home" value="D:\apache-jmeter-5.6.3" />
    <property name="report.title" value="接口自动化测试"/>
    <!-- jmeter生成jtl格式的结果报告的路径--> 
    <property name="jmeter.result.jtl.dir" value="E:\ants\jtl" />
    <!-- jmeter生成html格式的结果报告的路径-->
    <property name="jmeter.result.html.dir" value="E:\ants\html" />
	<!-- 【详细报告】jmeter生成html格式的详细报告的路径-->
	<property name="jmeter.result.html.dir1" value="report" />
    <!-- 生成的报告的前缀-->  
    <property name="ReportName" value="接口自动化汇总报告" />
	<property name="ReportName1" value="接口自动化详细报告" />
    <property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/${ReportName}.jtl" />
    <property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}.html" />
	<!-- 【详细报告】详细报告的文件名-->
    <property name="jmeter.result.htmlName1" value="${jmeter.result.html.dir1}/${ReportName1}.html" />
    <target name="run">
	   <!--antcall target="delete" /-->
        <antcall target="test" />
        <antcall target="report" />
    </target>
	
    <!-- 该命令用来删除今天已经执行过的jtl,防止旧数据重叠
	<target name ="delete">
		<delete file="${jmeter.result.jtl.dir}/${ReportName}${env.BUILD_ID}.jtl"/>
	</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:\ants" 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-report_21.xsl">
              <param name="dateReport" expression="${report.datestamp}"/>
       </xslt>
	   <!-- 【详细报告】指定详细报告模板文件-->
	   <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>
	 

                <!-- 因为上面生成报告的时候,不会将相关的图片也一起拷贝至目标目录,所以,需要手动拷贝 --> 
        <copy todir="${jmeter.result.html.dir}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
		<!-- 【详细报告】拷贝图片到目标目录-->
		<copy todir="${jmeter.result.html.dir1}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
    </target>
</project>

3、配置库文件

3.1、为什么需要配置库文件?

我们刚刚不是下载一个一个Ant吗?那么我们怎么让Ant和Jmeter关联呢?这个问题就是我们为何需要配置库文件的原因了。

3.2、如何配置让他们关联?做以下两步

1)把 jmeter.results.shanhe.me.xsl 文件,复制到 你的JMeter安装目录 → extras文件夹

2)把 你的JMeter安装目录 → extras文件夹 里的 ant-jmeter-1.1.1.jar 文件,复制到 你的Ant安装目录 → lib文件夹

注意:jmeter.results.shanhe.me.xsl 属于第三方美化版详细报告模板,JMeter原始安装包无此文件,需单独下载


3.3 开始实操

1)去下载jmeter.results.shanhe.me.xsl文件

下载地址:jmeter.results.shanhe.me.xsl文件

当然,如果不下载就直接使用如下源码

xml 复制代码
<?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) &gt; 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>

新建文本文档-》把你上面全部代码完整复制粘贴进去-》关闭文档-》重命名文件-》文件名:jmeter.results.shanhe.me.xsl(一定要改后缀 .xsl,不要保留 .txt)

2)把jmeter.results.shanhe.me.xsl的文件拷贝到jmeter的D:\apache-jmeter-5.6.3\extras目录。
3)把jmeter的D:\apache-jmeter-5.6.3\extras目录下的ant-jmeter-1.1.1.jar拷贝到ant的lib目录

3.4、配置全局变量jmeter.propties文件

为什么需要配置jmeter.propties文件?

还记得之前我们将jmeter.save.saveservice.output_format=csv 改为了csv对吧

现在得重新改,改为xml格式,因为我们的Ant只支持格式:jmeter.save.saveservice.output_format=xml,所以修改:bin/jmeter.propties


3.5、重新打开dos窗口,然后输入ant命令执行脚本

1)定位到我们的Jmeter脚本的目录(.jmx)
2)直接输入ant命令并解释执行逻辑
3)查看结果
4)我们看html目录并点击里面的html文件看看



5)我们主要看的就是详细报告



5)信息报告中的信息修复

但是你会发现一个问题,我们的请求响应里面那些数据,他的详细信息没有显示出来。

其实就是我们之前用的bulid.xml文件缺少了一些必要的属性配置,那下面是我们修正后的正确配置模版👇

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>

    <!-- ====================== 【所有必须修改的路径,全部在这里!】 ====================== -->
    <property name="jmeter.home" value="【这里填你的JMeter安装目录,例如:D:\apache-jmeter-5.6.3】" />
    <property name="report.title" value="接口自动化测试"/>
    <property name="jmeter.result.jtl.dir" value="【这里填jtl文件存放目录,例如:E:\ants\jtl】" />
    <property name="jmeter.result.html.dir" value="【这里填汇总报告目录,例如:E:\ants\html】" />
    <property name="jmeter.result.html.dir1" value="report" />
    <property name="ReportName" value="接口自动化汇总报告" />
    <property name="ReportName1" value="接口自动化详细报告" />
    <!-- ================================================================================ -->

    <property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/${ReportName}.jtl" />
    <property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}.html" />
    <property name="jmeter.result.htmlName1" value="${jmeter.result.html.dir1}/${ReportName1}.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}">
            <testplans dir="【这里填你的jmx脚本所在目录,例如:E:\ants】" includes="*.jmx" />

            <!-- 以下是固定配置,用来保存请求、响应详情,不要修改 -->
            <property name="jmeter.save.saveservice.output_format" value="xml"/>
            <property name="jmeter.save.saveservice.response_data" value="true"/>
            <property name="jmeter.save.saveservice.samplerData" value="true"/>
            <property name="jmeter.save.saveservice.requestHeaders" value="true"/>
            <property name="jmeter.save.saveservice.responseHeaders" value="true"/>
            <property name="jmeter.save.saveservice.url" value="true"/>
        </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-report_21.xsl">
              <param name="dateReport" expression="${report.datestamp}"/>
       </xslt>
	   
       <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>

        <copy todir="${jmeter.result.html.dir}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
        <copy todir="${jmeter.result.html.dir1}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
    </target>
</project>

我自己的👇

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!-- =========================================================
     整个Ant构建脚本的根节点,定义项目名称、默认执行目标、当前目录
     ========================================================= -->
<project name="ant-jmeter-test" default="run" basedir=".">

    <!-- 引入系统环境变量 -->
    <property environment="env"/>

    <!-- 定义时间戳格式,用于生成带时间的报告 -->
    <tstamp>
        <format property="time" pattern="yyyy_MM_dd_hh_mm" />
    </tstamp>

    <!-- =====================================================
         【1、基础路径配置】:你只需要改这里的路径
         ===================================================== -->
    <!-- JMeter的安装根目录 -->
    <property name="jmeter.home" value="D:\apache-jmeter-5.6.3" />
    <!-- 测试报告标题 -->
    <property name="report.title" value="接口自动化测试"/>

    <!-- JMeter生成jtl结果文件的存放目录 -->
    <property name="jmeter.result.jtl.dir" value="E:\ants\jtl" />
    <!-- 汇总HTML报告存放目录 -->
    <property name="jmeter.result.html.dir" value="E:\ants\html" />
    <!-- 详细HTML报告存放目录 -->
    <property name="jmeter.result.html.dir1" value="report" />

    <!-- 报告文件名前缀 -->
    <property name="ReportName" value="接口自动化汇总报告" />
    <property name="ReportName1" value="接口自动化详细报告" />

    <!-- 拼接最终jtl文件完整路径 -->
    <property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/${ReportName}.jtl" />
    <!-- 拼接最终汇总报告完整路径 -->
    <property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}.html" />
    <!-- 拼接最终详细报告完整路径 -->
    <property name="jmeter.result.htmlName1" value="${jmeter.result.html.dir1}/${ReportName1}.html" />

    <!-- =====================================================
         【2、总执行入口】:默认执行run目标,先跑测试,再生成报告
         ===================================================== -->
    <target name="run">
        <!-- 调用test目标执行JMeter脚本 -->
        <antcall target="test" />
        <!-- 调用report目标生成测试报告 -->
        <antcall target="report" />
    </target>

    <!-- =====================================================
         【3、执行JMeter性能测试脚本】⭐⭐⭐【核心配置区】⭐⭐⭐
         ===================================================== -->
    <target name="test">
        <!-- 定义Ant调用JMeter任务的类(固定写法) -->
        <taskdef name="jmeter" classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask" />
        
        <!-- 执行JMeter命令,指定JMeter目录和结果文件输出路径 -->
        <jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
            <!-- 指定要运行的.jmx脚本:扫描E:\ants目录下所有脚本 -->
            <testplans dir="E:\ants" includes="*.jmx" />

            <!-- =====================================================
                 ⭐⭐⭐【你之前缺失的关键配置:开启数据保存】⭐⭐⭐
                 以下配置作用:让JMeter把【请求、响应、头、参数】全部写入jtl文件
                 不加这些,你的详细报告永远是空的!!!
                 ===================================================== -->
            <!-- 输出格式为XML(必须,报告解析需要) -->
            <property name="jmeter.save.saveservice.output_format" value="xml"/>
            <!-- 保存响应数据(Response Data)→ 你截图空白的核心 -->
            <property name="jmeter.save.saveservice.response_data" value="true"/>
            <!-- 保存请求样本数据 -->
            <property name="jmeter.save.saveservice.samplerData" value="true"/>
            <!-- 保存请求头(Request Headers) -->
            <property name="jmeter.save.saveservice.requestHeaders" value="true"/>
            <!-- 保存响应头(Response Headers) -->
            <property name="jmeter.save.saveservice.responseHeaders" value="true"/>
            <!-- 保存请求URL -->
            <property name="jmeter.save.saveservice.url" value="true"/>

        </jmeter>
    </target>

    <!-- =====================================================
         【4、XSLT解析依赖路径】:生成HTML报告需要的jar包
         ===================================================== -->
    <path id="xslt.classpath">
        <!-- 引入JMeter自带的xalan解析器 -->
        <fileset dir="${jmeter.home}/lib" includes="xalan*.jar"/>
        <!-- 引入序列化工具包 -->
        <fileset dir="${jmeter.home}/lib" includes="serializer*.jar"/>
    </path>

    <!-- =====================================================
         【5、生成HTML测试报告】:汇总报告 + 详细报告
         ===================================================== -->
    <target name="report">
        <!-- 报告头部显示的时间 -->
        <tstamp> <format property="report.datestamp" pattern="yyyy/MM/dd HH:mm" /></tstamp>

        <!-- =====================================================
             生成【汇总报告】(简洁版)
             使用JMeter官方默认模板
        ===================================================== -->
        <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>

        <!-- =====================================================
             生成【详细报告】(完整版)
             使用你自定义的美化模板
        ===================================================== -->
        <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>

        <!-- =====================================================
             拷贝报告需要的图标文件
        ===================================================== -->
        <!-- 拷贝图标到汇总报告目录 -->
        <copy todir="${jmeter.result.html.dir}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
        <!-- 拷贝图标到详细报告目录 -->
        <copy todir="${jmeter.result.html.dir1}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png" />
                <include name="expand.png" />
            </fileset>
        </copy>
    </target>

</project>

然后我们再重新打开dos窗口,进行执行,👇

然后我们重新打开我们的详细报告看一下👇

此时报告上就会有详细信息👇


一旦有这些详细信息,包括我们的调试,我们也核心看这些信息。


上述就是我们用JMeter集成Ant来生成我们接口测试的报告,但是这种报告相对来说是一般的,我们现在做接口自动化用的最多的是Allure报告,而我们的JMeter它也能够集成Allure报告,我们接下来看👇


三、精通Jmeter接口测试集成Ant生成Allure报告

那么如何集成呢?步骤是什么呢?不要着急,跟着我的步骤一步一步的做,一定能做成功。

1、下载Allure并解压,解压后把allure的bin目录配置到PATH目录

这一步的话跟我们上述的ant差不多,反正就是下载压缩包,然后解压,然后再配置bin目录到我们的PATH目录中去,到时候执行相应的命令的时候,我们的操作系统能够找得到。

1.1、下载

下载地址:https://github.com/allure-framework/allure2/releases

版本要求:2.24以上

1.2、解压


1.3、配置环境变量

验证是否成功:


2、安装Allure报告的依赖库:allure-pytest

如何去安装呢?在我们的dos窗口里面输入以下命令:

bash 复制代码
pip install allure-pytest

3、配置ant的编译文件build.xml以及jmeter2allure.py文件

3.1、ant的编译文件build.xml

xml 复制代码
<!-- =====================================================
     Ant 项目配置:
     name:项目名称
     basedir=".":脚本当前目录
     default="execute-scripts":默认执行的任务
     ===================================================== -->
<project name="ShellScriptExecution" basedir="." default="execute-scripts">

    <!-- ====================== 【必须修改的3个路径】 ====================== -->
    <!-- JMeter 安装根目录 -->
    <property name="jmeter.home" value="【这里填你的JMeter安装路径】"/>
    <!-- Allure 报告工具安装目录 -->
    <property name="allure.home" value="【这里填你的Allure安装路径】"/>
    <!-- Python 安装目录(必须是python.exe所在目录) -->
    <property name="python.home" value="【这里填你的Python安装路径】"/>

    <!-- ====================== 以下内容 无需修改 ====================== -->
    <!-- 定义Ant调用JMeter任务的插件(固定写法) -->
    <taskdef
            name="jmeter"
            classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/>

    <!-- =====================================================
         任务1:clear
         作用:清空旧的测试文件,防止旧数据干扰
         删除:旧的result.jtl结果文件 + report报告目录
         ===================================================== -->
    <target name="clear">
        <delete file="result.jtl"/>
        <delete dir="report"/>
    </target>

    <!-- =====================================================
         任务2:runJMeter
         作用:执行当前目录下所有 .jmx 结尾的JMeter脚本
         生成结果文件:result.jtl
         自动开启请求/响应数据保存(保证Allure报告有详情)
         ===================================================== -->
    <target name="runJMeter">
        <jmeter jmeterhome="${jmeter.home}" resultlog="result.jtl">
            <!-- 执行当前目录下所有jmx脚本 -->
            <testplans dir="./" includes="*.jmx"/>

            <!-- 固定配置:保存请求、响应、Header,让报告有详情 -->
            <property name="jmeter.save.saveservice.output_format" value="xml"/>
            <property name="jmeter.save.saveservice.response_data" value="true"/>
            <property name="jmeter.save.saveservice.samplerData" value="true"/>
            <property name="jmeter.save.saveservice.requestHeaders" value="true"/>
            <property name="jmeter.save.saveservice.responseHeaders" value="true"/>
        </jmeter>
    </target>

    <!-- =====================================================
         任务3:makeAllure
         作用:调用python工具,把jtl文件转换成Allure报告
         ===================================================== -->
    <target name="makeAllure">
        <exec executable="cmd" dir="${basedir}">
            <arg value="/c"/>                    <!-- 执行cmd命令 -->
            <arg value="${python.home}/python.exe"/>  <!-- 调用Python -->
            <arg value="-m"/>                   <!-- 运行模块 -->
            <arg value="jmeter2allure"/>         <!-- jtl转allure工具 -->
            <arg value="result.jtl"/>            <!-- 输入结果文件 -->
            <arg value="report"/>                <!-- 输出报告目录 -->
            <arg value="${allure.home}/bin/allure"/>  <!-- allure命令路径 -->
        </exec>
    </target>

    <!-- =====================================================
         总入口任务:execute-scripts
         执行顺序:先清空 → 再运行JMeter → 最后生成Allure报告
         ===================================================== -->
    <target name="execute-scripts" depends="clear,runJMeter,makeAllure">
    </target>

</project>

3.1、jmeter2allure.py文件

py 复制代码
# ==========================================
# 脚本功能:
# JMeter 的 .jtl 结果文件 → 自动转成 Allure 精美HTML报告
# 实现:读取jtl → 生成pytest测试用例 → 运行 → 生成Allure单页报告
# ==========================================

"""
# 依赖安装命令(只需要装一次)
pip install pytest allure-pytest -i https://pypi.tuna.tsinghua.edu.cn/simple/
"""

# 导入Python解析XML的库
import xml.etree.cElementTree as ET

# 导入json、操作系统、唯一ID、系统参数、文件路径库
import json, os, uuid, sys
from pathlib import Path

# ==========================================
# 核心递归函数:遍历JMeter的jtl(XML)文件
# 读取:接口名称、请求、响应、断言、模块、功能模块
# 自动生成 pytest + allure 测试用例代码
# ==========================================
def checkChildren(
        xmlObject,    # XML节点对象
        checkString,  # 要查找的节点:httpSample
        num,          # 层级编号
        result,       # 存放解析出来的结果
        demoFile,     # 要生成的.py文件路径
        featureIndex, # 一级模块编号
        storyIndex    # 二级模块编号
):
    # 遍历XML所有子节点
    for children in xmlObject:
        try:
            # 第一层:解析一级模块(feature)
            if num == 1 and children.attrib["sby"] != "0":
                featureIndexStr = "#" + str(featureIndex) + " " if featureIndex >= 10 else "#0" + str(featureIndex) + " "
                result["feature"] = featureIndexStr + children.attrib["lb"]
                featureIndex += 1

            # 第二层及以下:解析二级模块(story)
            if num >= 2 and children.tag == "sample":
                storyIndexStr = "#" + str(storyIndex) + " " if storyIndex >= 10 else "#0" + str(storyIndex) + " "
                result["story"] = storyIndexStr + children.attrib["lb"]
                storyIndex += 1
        except:
            pass

        # ==========================================
        # 找到接口节点 httpSample,开始解析数据
        # ==========================================
        if children.tag == checkString:
            # 用例名称
            result["case_name"] = children.attrib["lb"]

            # 遍历接口的请求头、响应头、请求体、响应体、断言
            for httpSampleChildren in children:
                result[httpSampleChildren.tag] = httpSampleChildren.text

                # 如果是断言结果,单独解析
                if httpSampleChildren.tag == "assertionResult":
                    for assertionResultChildren in httpSampleChildren:
                        result[assertionResultChildren.tag] = assertionResultChildren.text

            # ==========================================
            # 从解析结果中取出需要展示到报告里的字段
            # ==========================================
            feature = result.get("feature")          # 一级模块
            story = result.get("story")              # 二级模块
            case_name = result.get("case_name")      # 用例名称
            URL = result.get("java.net.URL")         # 请求URL
            method = result.get("method")            # 请求方法
            requestHeader = result.get("requestHeader")      # 请求头
            queryString = result.get("queryString")          # 请求参数
            responseData = result.get("responseData")        # 响应数据
            failureMessage = result.get("failureMessage")    # 失败信息
            failure = result.get("failure", "false")          # 是否失败

            # ==========================================
            # 拼接 Allure 测试用例代码(字符串)
            # ==========================================
            storyString = f"@allure.story('{story}') # 二级目录" if story else ""

            pyString = f"""
@allure.feature('{feature}') # 一级目录{storyString}
@allure.title("{case_name}")
def test_allure_report_{str(uuid.uuid1()).replace('-', '')}():
    with allure.step('请求url:{URL}'):
        print('请求url:{URL}')
    with allure.step('请求方法:{method}'):
        print('请求方法:{method}')
    with allure.step('请求头:{requestHeader}'):
        print('请求头:{requestHeader}')
    with allure.step('''请求数据:{queryString}'''):
        print('''请求数据:{queryString}''')
    with allure.step('''接口返回:{responseData}'''):
        print('''接口返回:{responseData}''')
    with allure.step('''断言结果:{failureMessage}'''):
        print('''断言结果:{failureMessage}''')
    assert "{failure}" == 'false'
"""

            # ==========================================
            # 把生成的用例写入 .py 文件
            # ==========================================
            with open(demoFile, "a", encoding="utf-8") as c:
                c.write(pyString)

        # 递归继续查找子节点
        else:
            checkChildren(
                children,
                checkString,
                num + 1,
                result,
                demoFile,
                featureIndex,
                storyIndex,
            )

# ==========================================
# 主函数入口
# ==========================================
if __name__ == "__main__":
    # 接收命令行参数:python 脚本.py jtl文件 报告目录 allure路径
    _self_path, jtl_path, report_path, allure_path, *_ = sys.argv

    # 路径转换为Path对象(方便操作)
    jtl_path = Path(jtl_path)
    report_path = Path(report_path)
    report_resource = report_path / "resource"  # allure原始数据
    report_html = report_path / "html"          # 最终HTML报告
    tmp_file_path = Path("test.py")             # 临时生成的pytest用例文件

    # ==========================================
    # 1. 创建临时 test.py 文件,写入头部引用
    # ==========================================
    with open(tmp_file_path, "w", encoding="utf-8") as demo:
        demo.write("""
# -*- coding: utf-8 -*-
import allure
""")

    # ==========================================
    # 2. 解析 jtl XML 文件
    # ==========================================
    tree = ET.parse(str(jtl_path))
    root = tree.getroot()

    # ==========================================
    # 3. 调用递归函数,遍历XML生成测试用例
    # ==========================================
    checkChildren(root, "httpSample", 1, {}, str(tmp_file_path), 1, 1)

    # ==========================================
    # 4. 执行 pytest 生成 allure 原始数据
    # ==========================================
    pyString = f"{sys.executable} -m pytest --capture=sys {tmp_file_path}  --alluredir {report_resource} --clean-alluredir -qq"
    os.system(pyString)

    # ==========================================
    # 5. 调用 allure 生成 单页HTML报告
    # ==========================================
    alSring = f"{allure_path} generate {report_resource} -o {report_html} --single-file --clean"
    os.system(alSring)

    # ==========================================
    # 6. 删除临时 test.py 文件
    # ==========================================
    tmp_file_path.unlink(True)

    # ==========================================
    # 7. 自动打开生成好的报告
    # ==========================================
    os.system(f'{report_html / "index.html"}')

3.3、实操

两个文件必须放在【JMeter 脚本(.jmx)所在的同一个目录】!

1)首先在我们的脚本同级目录新建一个bulid.xml(名字必须严格这样)
2)将上述xml内容拷贝进去并做配置

我自己的配置如下:


那么python的目录怎么获取?如下👇输入where python


代码

xml 复制代码
<!-- =====================================================
     Ant 项目配置:
     name:项目名称
     basedir=".":脚本当前目录
     default="execute-scripts":默认执行的任务
     ===================================================== -->
<project name="ShellScriptExecution" basedir="." default="execute-scripts">

    <!-- ====================== 【必须修改的3个路径】 ====================== -->
    <!-- JMeter 安装根目录 -->
    <property name="jmeter.home" value="D:\apache-jmeter-5.6.3"/>
    <!-- Allure 报告工具安装目录 -->
    <property name="allure.home" value="D:\allure-2.39.0"/>
    <!-- Python 安装目录(必须是python.exe所在目录) -->
    <property name="python.home" value="D:\PythonProject"/>

    <!-- ====================== 以下内容 无需修改 ====================== -->
    <!-- 定义Ant调用JMeter任务的插件(固定写法) -->
    <taskdef
            name="jmeter"
            classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/>

    <!-- =====================================================
         任务1:clear
         作用:清空旧的测试文件,防止旧数据干扰
         删除:旧的result.jtl结果文件 + report报告目录
         ===================================================== -->
    <target name="clear">
        <delete file="result.jtl"/>
        <delete dir="report"/>
    </target>

    <!-- =====================================================
         任务2:runJMeter
         作用:执行当前目录下所有 .jmx 结尾的JMeter脚本
         生成结果文件:result.jtl
         自动开启请求/响应数据保存(保证Allure报告有详情)
         ===================================================== -->
    <target name="runJMeter">
        <jmeter jmeterhome="${jmeter.home}" resultlog="result.jtl">
            <!-- 执行当前目录下所有jmx脚本 -->
            <testplans dir="./" includes="*.jmx"/>

            <!-- 固定配置:保存请求、响应、Header,让报告有详情 -->
            <property name="jmeter.save.saveservice.output_format" value="xml"/>
            <property name="jmeter.save.saveservice.response_data" value="true"/>
            <property name="jmeter.save.saveservice.samplerData" value="true"/>
            <property name="jmeter.save.saveservice.requestHeaders" value="true"/>
            <property name="jmeter.save.saveservice.responseHeaders" value="true"/>
        </jmeter>
    </target>

    <!-- =====================================================
         任务3:makeAllure
         作用:调用python工具,把jtl文件转换成Allure报告
         ===================================================== -->
    <target name="makeAllure">
        <exec executable="cmd" dir="${basedir}">
            <arg value="/c"/>                    <!-- 执行cmd命令 -->
            <arg value="${python.home}/python.exe"/>  <!-- 调用Python -->
            <arg value="-m"/>                   <!-- 运行模块 -->
            <arg value="jmeter2allure"/>         <!-- jtl转allure工具 -->
            <arg value="result.jtl"/>            <!-- 输入结果文件 -->
            <arg value="report"/>                <!-- 输出报告目录 -->
            <arg value="${allure.home}/bin/allure"/>  <!-- allure命令路径 -->
        </exec>
    </target>

    <!-- =====================================================
         总入口任务:execute-scripts
         执行顺序:先清空 → 再运行JMeter → 最后生成Allure报告
         ===================================================== -->
    <target name="execute-scripts" depends="clear,runJMeter,makeAllure">
    </target>

</project>
3)在脚本(.jmx)同级目录下创建jmeter2allure.py文件
4)将上述的jmter2allure.py文件代码粘贴进去

4、重新打开dos窗口,然后输入ant命令执行脚本

4.1、切换到对应的目录

4.2、输入ant命令并解释逻辑

4.3、执行ant命令看结果

1)错误排查
2)错误修改
3)修改之后的正确结果


4)出现问题

从上面的图我们也可以看出,我们这个报告里面又没有任何数据,甚至有乱码。那这是我们之前的代码配置错误,我们做了如下修改。

a.修改之后的通用模版build.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!-- ============================================================
    Ant 通用模板:JMeter 自动化执行 + Allure 报告生成
    使用方法:只修改【用户配置区】3 个路径,其余不动
============================================================= -->
<project name="ShellScriptExecution" basedir="." default="execute-scripts">

    <!-- ===================== 【用户配置区】请根据自己电脑修改 ===================== -->
    <!-- JMeter 安装路径 -->
    <property name="jmeter.home" value="【这里填JMeter安装路径】"/>
    <!-- Allure 安装路径 -->
    <property name="allure.home" value="【这里填Allure安装路径】"/>
    <!-- Python 安装路径 -->
    <property name="python.home" value="【这里填Python安装路径】"/>

    <!-- ===================== 【固定配置】以下无需修改 ===================== -->

    <taskdef
            name="jmeter"
            classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/>

    <target name="clear">
        <delete file="result.jtl"/>
        <delete dir="report"/>
    </target>

    <target name="runJMeter">
        <jmeter jmeterhome="${jmeter.home}" resultlog="result.jtl">
            <testplans dir="./" includes="*.jmx"/>
        </jmeter>
    </target>

    <target name="makeAllure">
        <exec executable="cmd" dir="${basedir}">
            <arg value="/c"/>
            <arg value="${python.home}/python.exe"/>
            <arg value="-m"/>
            <arg value="jmeter2allure"/>
            <arg value="result.jtl"/>
            <arg value="report"/>
            <arg value="${allure.home}/bin/allure"/>
        </exec>
    </target>

    <target name="execute-scripts" depends="clear,runJMeter,makeAllure">
    </target>

</project>
b.修改之后我自己的build.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!-- ============================================================
    Ant 构建脚本:自动化执行 JMeter 测试 + 生成 Allure 测试报告
    专属个人版(已配置你本机路径)
============================================================= -->
<project name="ShellScriptExecution" basedir="." default="execute-scripts">

    <!-- ===================== 【个人路径配置】仅这里需要根据自己电脑修改 ===================== -->
    <!-- JMeter 安装根目录 -->
    <property name="jmeter.home" value="D:\apache-jmeter-5.6.3"/>
    <!-- Allure 报告工具安装目录 -->
    <property name="allure.home" value="D:\allure-2.39.0"/>
    <!-- Python 安装目录(必须是 python.exe 所在目录) -->
    <property name="python.home" value="D:\PythonProject"/>

    <!-- ===================== 【固定配置】以下内容无需修改 ===================== -->

    <!-- 注册 JMeter 任务插件,让 Ant 能调用 JMeter 执行脚本 -->
    <taskdef
            name="jmeter"
            classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/>

    <!-- ===================== 目标1:clear ===================== -->
    <!-- 作用:清空历史生成文件,避免旧数据干扰 -->
    <target name="clear">
        <!-- 删除旧的 jtl 结果文件 -->
        <delete file="result.jtl"/>
        <!-- 删除旧的 report 报告目录 -->
        <delete dir="report"/>
    </target>

    <!-- ===================== 目标2:runJMeter ===================== -->
    <!-- 作用:执行当前目录下所有 .jmx 脚本,生成 result.jtl 结果文件 -->
    <target name="runJMeter">
        <jmeter jmeterhome="${jmeter.home}" resultlog="result.jtl">
            <!-- 匹配执行当前目录所有 jmx 文件 -->
            <testplans dir="./" includes="*.jmx"/>
        </jmeter>
    </target>

    <!-- ===================== 目标3:makeAllure ===================== -->
    <!-- 作用:调用 Python 模块 jmeter2allure 解析 jtl,生成 Allure 报告 -->
    <target name="makeAllure">
        <exec executable="cmd" dir="${basedir}">
            <!-- 打开 cmd 执行命令 -->
            <arg value="/c"/>
            <!-- 使用你配置的 Python 解释器 -->
            <arg value="${python.home}/python.exe"/>
            <!-- 以模块方式运行 jmeter2allure -->
            <arg value="-m"/>
            <arg value="jmeter2allure"/>
            <!-- 传入参数1:jtl 结果文件 -->
            <arg value="result.jtl"/>
            <!-- 传入参数2:报告输出目录 -->
            <arg value="report"/>
            <!-- 传入参数3:allure 命令路径 -->
            <arg value="${allure.home}/bin/allure"/>
        </exec>
    </target>

    <!-- ===================== 总执行入口 ===================== -->
    <!-- 执行顺序:清空 → 运行JMeter → 生成报告 -->
    <target name="execute-scripts" depends="clear,runJMeter,makeAllure">
    </target>

</project>

c.修改之后的jmeter2allure.py
py 复制代码
# -*- coding: utf-8 -*-
"""
============================================================
JMeter jtl 结果文件 转 Allure HTML 测试报告
功能:
    1. 解析 JMeter 生成的 .jtl 文件
    2. 自动生成 pytest + allure 测试用例
    3. 调用 pytest 运行用例
    4. 调用 allure 生成 HTML 报告
============================================================
【依赖安装】
pip install pytest allure-pytest -i https://pypi.tuna.tsinghua.edu.cn/simple/
"""

# 导入 XML 解析库,用于解析 jtl 文件
import xml.etree.cElementTree as ET
# 用于处理字符串、JSON 转义
import json
# 用于执行系统命令
import os
# 用于生成唯一不重复的用例名
import uuid
# 用于获取命令行参数
import sys
# 用于路径处理
from pathlib import Path


def checkChildren(
        xmlObject, checkString, num, result, demoFile, featureIndex, storyIndex
):
    """
    递归函数:遍历 XML 节点,解析出接口、模块、用例、请求、响应、断言
    :param xmlObject: 当前 XML 节点
    :param checkString: 要匹配的节点名称(httpSample)
    :param num: 递归层级
    :param result: 存储解析结果
    :param demoFile: 要生成的测试用例文件路径
    :param featureIndex: 一级模块编号
    :param storyIndex: 二级用例编号
    """
    # 遍历当前节点下所有子节点
    for children in xmlObject:
        try:
            # 第一层节点:解析 一级模块(feature)
            if num == 1 and children.attrib["sby"] != "0":
                # 格式化模块编号
                featureIndexStr = "#" + str(featureIndex) + " " if featureIndex >= 10 else "#0" + str(featureIndex) + " "
                result["feature"] = featureIndexStr + children.attrib["lb"]
                featureIndex += 1

            # 第二层及以下:解析 二级用例(story)
            if num >= 2 and children.tag == "sample":
                storyIndexStr = "#" + str(storyIndex) + " " if storyIndex >= 10 else "#0" + str(storyIndex) + " "
                result["story"] = storyIndexStr + children.attrib["lb"]
                storyIndex += 1
        except:
            # 异常忽略,不影响整体解析
            pass

        # 匹配到接口请求节点 httpSample,开始解析接口数据
        if children.tag == checkString:
            # 用例名称
            result["case_name"] = children.attrib["lb"]

            # 遍历接口的请求头、响应头、请求体、响应体、断言
            for httpSampleChildren in children:
                result[httpSampleChildren.tag] = httpSampleChildren.text

                # 如果是断言结果,单独解析
                if httpSampleChildren.tag == "assertionResult":
                    for assertionResultChildren in httpSampleChildren:
                        result[assertionResultChildren.tag] = assertionResultChildren.text

            # 安全取值:存在则取,不存在为 None
            feature = result["feature"] if "feature" in result else None
            story = result["story"] if "story" in result else None
            case_name = result["case_name"] if "case_name" in result else None
            URL = result["java.net.URL"] if "java.net.URL" in result else None
            method = result["method"] if "method" in result else None
            requestHeader = result["requestHeader"] if "requestHeader" in result else None
            queryString = result["queryString"] if "queryString" in result else None
            responseData = result["responseData"] if "responseData" in result else None
            failureMessage = result["failureMessage"] if "failureMessage" in result else None
            failure = result["failure"] if "failure" in result else "false"

            # 优化断言结果显示
            if failure == "false":
                failureMessage = "断言成功"
            elif not failureMessage or failureMessage.strip() == "":
                failureMessage = "断言失败(无详细信息)"

            # 拼接 allure 二级目录注解
            storyString = "@allure.story('" + result["story"] + "') # 二级目录" if "story" in result else ""

            # ===================== 生成 pytest + allure 用例代码 =====================
            pyString = """
@allure.feature('{feature}') # 一级目录{story}
@allure.title("{case_name}")
def test_allure_report_{num}():
    with allure.step('请求url:{URL}'):
        print('请求url:{URL}')
    with allure.step('请求方法:{method}' ):
        print('请求方法:{method}' )
    with allure.step('请求头:{requestHeader}' ):
        print('请求头:{requestHeader}' )
    with allure.step('''请求数据:{queryString}'''):
        print('''请求数据:{queryString}''')
    with allure.step('''接口返回:{responseData}'''):
        print('''接口返回:{responseData}''')
    with allure.step('''断言结果:{failureMessage}'''):
        print('''断言结果:{failureMessage}''')
    assert "{failure}" == 'false' """.format(
                feature=feature,
                story=storyString,
                case_name=case_name,
                num=str(uuid.uuid1()).replace("-", ""),
                URL=URL,
                method=method,
                requestHeader=str(requestHeader).replace("\n", "").replace("\r", ""),
                queryString=str(json.dumps(queryString)).replace("'", '"'),
                responseData=str(json.dumps(responseData)).replace("'", '"'),
                failureMessage=str(failureMessage).replace("'", '"'),
                failure=failure,
            )

            # 将生成的用例写入测试文件
            with open(demoFile, "a", encoding="utf-8") as c:
                c.write(pyString)

        else:
            # 递归继续解析子节点
            checkChildren(
                children,
                checkString,
                num + 1,
                result,
                demoFile,
                featureIndex,
                storyIndex,
            )


if __name__ == "__main__":
    """
    主函数入口
    命令格式:python jmeter2allure.py 结果文件.jtl 报告目录 allure路径
    """
    # 获取命令行传入的 4 个参数
    _self_path, jtl_path, report_path, allure_path, *_ = sys.argv

    # 路径转换
    jtl_path = Path(jtl_path)
    report_path = Path(report_path)
    # allure 原始数据存放目录
    report_resource = report_path / "resource"
    # 最终 HTML 报告目录
    report_html = report_path / "html"
    # 临时生成的 pytest 用例文件
    tmp_file_path = Path("test.py")

    # 初始化临时用例文件,写入导包
    with open(tmp_file_path, "w", encoding="utf-8") as demo:
        demo.write("""
# -*- coding: utf-8 -*-
import allure
""")

    # 解析 jtl 文件
    tree = ET.parse(str(jtl_path))
    root = tree.getroot()

    # 递归解析 jtl,生成测试用例
    checkChildren(root, "httpSample", 1, {}, str(tmp_file_path), 1, 1)

    # ===================== 执行 pytest,生成 allure 原始数据 =====================
    pyString = f"{sys.executable} -m pytest --capture=sys {tmp_file_path}  --alluredir {report_resource} --clean-alluredir -qq"
    os.system(pyString)

    # ===================== 生成 HTML 报告 =====================
    alSring = f"{allure_path} generate   {report_resource}  -o {report_html} --single-file --clean"
    os.system(alSring)

    # 删除临时用例文件
    tmp_file_path.unlink(True)

    # 自动打开生成的报告
    os.system(f'{report_html / "index.html"}')

4.4、删除多余的文件重启dos窗口运行看结果


接下来解决详细信息的问题

4.5、解决没有详细信息问题

1)找到 JMeter 安装目录下的 bin/jmeter.properties,用文本编辑器打开,修改或添加以下配置项:

bash 复制代码
# 1. 输出格式必须为 XML(这个我们之前配置过了,此处不用管)
jmeter.save.saveservice.output_format=xml

# 2. 保存断言结果(用于记录失败原因)
jmeter.save.saveservice.assertion_results=all
jmeter.save.saveservice.assertion_results_failure_message=true

# 3. 保存请求 URL
jmeter.save.saveservice.url=true

# 4. 保存请求方法(GET/POST 等)
jmeter.save.saveservice.method=true          # 注意:您的片段里没有 method 这一行?如果缺了需手动添加

# 5. 保存请求主体数据(含表单、JSON、文件上传的元数据)
jmeter.save.saveservice.samplerData=true

# 6. 保存查询字符串(GET 参数)
jmeter.save.saveservice.queryString=true

# 7. 保存请求头
jmeter.save.saveservice.requestHeaders=true

# 8. 保存响应数据(接口返回的正文)
jmeter.save.saveservice.response_data=true

# 9. 保存响应头(可选,但有助于调试)
jmeter.save.saveservice.responseHeaders=true


2)重新运行ant查看结果

5、补充

现在如果想要手动打开报告的话,就去report目录的html目录中打开


四、精通Jmeter接口测试Jenkins持续集成

其实无论是我们的ant报告,还是我们的allure报告,我们的最终目的都是为了给我们的Jenkins持续集成。

那么如何去Jenkins持续集成,接下里就跟着我一步一步的实现。

1、安装Jenkins,建议去官网安装最新版本

1.1 下载

官网地址: https://www.jenkins.io/download/

Jenkins是一个web项目

兼容性问题:由于Jenkins是一个javaweb项目,那么得需要jdk,建议jdk要17及以上。

1.2 启动服务并访问

启动

对一个java项目,我们如何去启动呢?它是一个war包,那么我只需要运行这个war包就行。

1)首先得看你的jdk有没有下载配置好

java项目的运行需要的是jdk,这一点很重要,没有java环境的老铁得去配置一下,我们之前第一篇博客有说过。

2)运行war包,命令:java -jar 全路径+war包名称(带后缀)

比如我自己的是放在D盘,所以启动命令java -jar D:\jenkins.war

3)那么我就将jdk21下载好了,放在如下目录D:\java\Javajdk\jdk-21

4)去bin目录找到java命令

5)然后执行"jdk21的bin目录+java" -jar war包全路径+war包名称

比如我自己的是:"D:\java\Javajdk\jdk-21\bin\java" -jar D:\jenkins.war

访问

如何访问呢?

浏览器输入访问地址:http://localhost:8080

初始化密码,你启动的时候他会给你显示。

用户名默认是admin

密码初始话的时候如下图:

1)输入URL访问:http://localhost:8080

2)会告诉你是否安装插件


1.3 初始化安装插件

首先第一次登录的时候可以选择推荐的

然后他就在下载

把上述的插件安装完毕之后,他会给你一个提示框,让你创建一个管理员用户👇

1.4 修改密码👤

如果你不想创建,你就直接点使用admin账户继续就行,如上图所示:


然后后面就直接点击继续


修改密码:如下所示

然后我们就输入自己的密码即可比如123456

修改密码之后重新登录:

用户名admin

密码123456


1.5 修改插件的下载位置(可选)

下载完插件之后,你会发现在你的c盘里面会多了一些目录,这些目录就是为我们放我们插件的创建的,那如果你c盘空间不够的话,我们是可以进行一个修改的。如何去修改呢?接下来就跟着我一步一步的去做就行。嗯,如果老铁们嫌麻烦的话,就可以不用管这个步骤。

1). 停止当前运行的 Jenkins
  • 关闭运行 java -jar jenkins.war 的命令行窗口(按 Ctrl + C 或直接关闭)。
2). 迁移原有数据(可选)

如果你已经使用过 Jenkins(创建了任务、安装了插件),需要把旧数据复制到新目录。

  • 旧目录:C:\Users\用户名\.jenkins
  • 新目录(例子):D:\jenkins_home

操作:

  • 在 D 盘创建文件夹 jenkins_home
  • C:\Users\用户名\.jenkins 下的所有内容 复制到 D:\jenkins_home(包括 config.xmlpluginsworkspace 等)

如果 Jenkins 是全新安装(刚跑通初始化),可以不用复制旧数据,直接让 Jenkins 在新目录重新初始化。

3). 启动时指定新的家目录
方法:修改系统环境变量(永久)
  • 右键"此电脑" → 属性 → 高级系统设置 → 环境变量
  • 在"系统变量"或"用户变量"中新建:
    • 变量名:JENKINS_HOME
    • 变量值:D:\jenkins_home
  • 确定后重启 cmd,直接运行 "D:\java\Javajdk\jdk-21\bin\java" -jar D:\jenkins.war 就会自动使用该目录。


    Jenkins 现在已经成功将家目录迁移到 D:\jenkins_home

1.6 下载HTML报告的插件和Allure报告的插件

具体步骤

1、选择设置

2、在 Manage Jenkins 页面中,点击 "插件管理" (图标是拼图块,文字是"添加、删除、禁用或启用Jenkins功能扩展插件")。

3、进入插件管理页面后,点击 "Available plugins" (可用插件)选项卡。

4、在右上角的搜索框中分别搜索以下两个插件,并勾选:

  • HTML Publisher → 找到 HTML Publisher ,勾选。

  • Allure → 找到 Allure ,勾选。

安装完成后验证



注意:Jenkins得通过插件才能完成后续的操作,所以你得下载插件

况且很多的插件你是需要做配置的,那么怎么去配置如下所示👇


2、插件配置

我们并不是说有些插件你下载之后就是可以直接使用,而是需要做一些配置,比如我们的Allure需要配置才能使用,而且这种情况核心的配置步骤我们可以问AI很快就能做完。

当然我们的html报告插件是不需要配置的。

你看有些插件需要配置,有些插件不需要配置,那你如何去判断,他们需不需要配置呢?👇

对于不确定的插件,可以问AI:"Jenkins 的 [插件名] 插件安装后需要额外配置吗?怎么配置?"


那么接下来我们就对Allure插件进行配置


Allure插件配置

步骤如下:

由于Jenkins 我们的这个版本不断的在更新,所以到时候如果没有出现过如下所示的界面,那大家可以问AI。

1) 点击设置
2)点击全局工具配置

3)往下翻找到【Allure Commandline 安装】

4)点击【+新增Allure Commandline】然后起一个别名

5)然后他问你是否需要自动安装,那把那个√给去掉,我们不需要自动安装,因为我们的本地已经装好了。

6)在本地将我们的allure安装路径给复制上去,并保存

从这个过程我们也看到一个小规律,就是说你这些工具是需要下载到本地的,然后我们所谓的配置无非就是把本地的安装路径给配置上去而已,所以需不需要配置,就得看你这个工具是否需要下载。


接下来我们就可以使用Jenkins执行我们的Jmeter测试脚本了,然后生成对应的报告👇


3、Jenkins的配置

还是一样的,跟我一步一步的做,如果界面跟我不同,那就问AI,因为他版本在更新。

3.1 点击【+新建Item】

3.2 起一个名字

3.3 选择任务类型:自由风格的(Freestyle project)

于是会来到这个界面

3.4 做项目(任务)配置:点击高级,勾选自定义工作空间

为什么我们要设置自己的工作空间呢?

因为我们想让Jenkins这个工具为我们去执行jmeter的测试脚本,所以你得告诉他你的测试脚本在哪里,如果你不告诉他他去执行什么呢,对吧?同时到时候我们生成的测试报告会让他放到我们工作空间中去(而里面的具体目录就是我们的bulix.xml那些文件所去指定的操作。)

3.5 指定Jmeter测试脚本所在的目录

3.6 点击【增加构建步骤】:选择执行windows的批处理命令并输入执行的命令

然后输入我们要执行的命令,我们要执行的命令很简单:ant👇

3.7 点击保存之后呢,我们会跳到如下界面【开始执行】

1)保存
2)保存后跳转如下👇
3)首页
4)点击运行
5)结果

同时还会自动打开报告(py代码中写的有)

3.8 其他介绍

1)√代表执行成功
2)点击项目名称进去看详情页

如果到时候报错,我们是可以查看我们的控制台的。

3)查看控制台



3.9 Jenkins去集成我们的Allure报告

1)我们要生成对应的报告
2)点击配置
3)找到构建之后操作步骤->allure report
4)勾选之后会有如下页面
5)配置JSON报告所在的地址,细节看图
6)往下翻,找到高级展开,配置html报告的地址

向下找,找到Report path:报告路径,细节🔍如下👇

7)点击保存,再执行一次

执行

8)我们的报告集成成功

报告如下


3.10 Jenkins去集成我们的HTML报告

上面我们说的是让我们Jenkins去集成我们的Allure报告,但是我们如果没有Allure报告的话,我发现只是一个单纯的html报告,那我们如何去集成呢?

还是老规矩跟着我一步一步的去做:

1)回到我们的最初的ant生成html报告的build.xml,如下
2)ok,然后我们要去执行他
3)结果,找到html报告页面

然后找到我们的报告,报告里面有个html报告,我们打开后会看到如下的一个界面

界面如下

他不是我们的Allure报告,他只是一个单纯的html报告,那我们接下来就说Jenkins如何去集成这个html报告。

4)选择配置
5)选择构建HTML报告
6)点击新增
7)配置路径+名字+标题+保存
8)重新运行
9)查看报告

首先点击项目名称查看详细信息

在详情页中选择我们的HTML报告

结果如下

但是你会发现我们的那些样式缺失了,那如何给他找回来呢?继续向下看⬇️

10)修复报告样式

没有样式,我们就装一个groovy就好了,在哪装?如下⬇️


下载 Groovy

从官网下载二进制包:https://groovy.apache.org/download.html

解压

配置环境变量

然后就向上述一样进行插件的配置

让我们重启Jenkins

然后输入这个命令"D:\java\Javajdk\jdk-21\bin\java" -Dhudson.model.DirectoryBrowserSupport.CSP= -jar D:\jenkins.war

通用命令公式

bash 复制代码
[Java执行路径] -Dhudson.model.DirectoryBrowserSupport.CSP= -jar [Jenkins war包路径]

各部分解释

组成部分 含义 用户需要替换成什么
[Java执行路径] Java 命令的完整路径(如果 java 已加入系统 PATH,可只写 java 例如:/usr/bin/javaC:\Program Files\Java\jdk-21\bin\java.exe
-Dhudson.model.DirectoryBrowserSupport.CSP= JVM 参数,用于禁用 Jenkins 的安全策略,让 HTML 报告显示样式。等号后面空着即可 固定写法,不需要替换
-jar Java 命令的参数,表示运行一个可执行的 jar 包 固定写法
[Jenkins war包路径] 你下载的 jenkins.war 文件存放的完整路径 例如:/home/user/jenkins.warD:\jenkins.war

接下来我们要把那些报告全部给删了,重新生成

点击运行

然后我们继续查看我们的html报告

报告结果


3.11 定时自定执行脚本

我们上述执行脚本都是用我们的手动去执行的,那我们可以设置一个定时器,让它自动的去执行我们的脚本。

1)点击配置
2)☑️日程表
3)输入定时时间*表示

那你设置定时器之后,你服务器也不要关闭,如果服务器关闭,他就不会去执行。


OK~~ 那么本期的分享就到此结束啦,干货很多,老铁们看到最后,不要忘了给俺点个免费的赞👍和关注啊,你的关注支持是我继续创作最大的动力


五、写在最后

🎯 看到这里,辛苦啦!歇一歇,喝口水,让眼睛放松一下
这篇笔记我写了很久,如果对你有哪怕一点点帮助,请点一下「关注」
后面我还会继续分享很多自学干货笔记,都是自己边学边总结的,依然会是零基础能看懂、每一步都截图的那种详细笔记
我是学生,没什么能送你的,只能保证每一篇都认真写、不藏私。
我能给的,就是一篇一篇亲手整理的、零基础也能看懂的干货笔记。
你的每一次关注,都是我继续写下去的动力。
谢谢你的时间,我们下一篇笔记见 👇
(如果这篇笔记有写错的地方,也请一定在评论区告诉我,我会第一时间修改)

下一篇见咯,兄弟们~~


相关推荐
电商API_180079052472 小时前
如何实现批量化自动化获取淘宝商品详情数据?爬虫orAPI?
大数据·c++·爬虫·自动化
晨+燕2 小时前
JMeter中如何定位到某个具体的类来自于哪个jar包
python·jmeter·jar
zhensherlock2 小时前
Protocol Launcher 系列:Working Copy 提交与同步全攻略
javascript·git·typescript·node.js·自动化·github·js
天空属于哈夫克33 小时前
企微自动化:API接口的私有化部署架构
运维·架构·自动化
TSINGSEE3 小时前
零代码自动化AI算法训练革命:企业级私有化部署DLTM自动化AI训练服务器,告别算法依赖
人工智能·深度学习·算法·机器学习·自动化·ai大模型
微刻时光3 小时前
影刀RPA:嵌套循环深度解析与实战指南
人工智能·python·机器人·自动化·rpa·影刀rpa
TechMasterPlus3 小时前
Claude Code CLI 使用教程:从安装到项目自动化实践
运维·自动化
William Dawson3 小时前
Jenkins 操作文档及使用方法(新手入门\+实战详解)
运维·jenkins
梦想的旅途23 小时前
企微自动化办公:实现外部群聊的高级交互逻辑
运维·数据库·自动化·企业微信·rpa