百度网盘地址

链接:https://pan.baidu.com/s/1vit5y3kNMY7zsFLOmIzeCw
提取码:apia

一、安装jdk

请参考:
https://blog.csdn.net/m0_46627257/article/details/123087497

二、安装jmeter

1、解压jmeter配置环境变量到bin目录下详情参考jdk
2、windows+r输入cmd进入控制台输入jmeter -v出现一下画面表示配置成功
在这里插入图片描述

三、安装ant

1、解压ant配置环境变量配置到bin目录下
2、windows+r输入cmd进入控制台输入ant -v出现一下画面表示配置成功
在这里插入图片描述

四、新建钉钉机器人

1、新建钉钉机器人,群管理有个智能群助手,添加机器人
在这里插入图片描述
选择自定义机器人
在这里插入图片描述
点击添加机器人
在这里插入图片描述
2、钉钉机器人安全文档https://developers.dingtalk.com/document/robots/customize-robot-security-settings
最多可以设置10个关键词,消息中至少包含其中1个关键词才可以发送成功。
例如添加了一个自定义关键词:监控报警,则这个机器人所发送的消息,必须包含监控报警这个词,才能发送成功。
在这里插入图片描述
添加机器人成功,就会生成一个key(Webhook),切记这个key,后面会用到。
在这里插入图片描述

五、修改jmeter配置文件

1、将jmeter目录下extras文件夹下的ant-jmeter-1.1.1.jar复制到ant中\lib目录下
2、修改jmeter中bin目录下的jmeter.properties
在这里插入图片描述
3、修改jmeter中extras目录下的build.xml文件,复制如下内容替换自己的build.xml文件即可,脚本路径自己修改

<project name="JMeter-Test" default="run" basedir=".">

  <!-- 初始化时间戳 -->
  <tstamp>
    <format property="time" pattern="_yyyy_MMdd_HHmmss" />
  </tstamp>
  
  <!-- 设置工作空间路径 -->
  <property name="workspace" value="D:\testtest\testxml" />
  <property name="workspace1" value="D:\testtest" />
  
  <!-- JMeter 目录 -->  
  <property name="jmeter.home" value="D:\apache-jmeter-5.4.3\apache-jmeter-5.4.3" />
  <property name="report.title" value="接口测试"/>
  
  <!-- 结果报告目录 --> 
  <property name="jmeter.result.jtl.dir" value="${workspace}" />
  <property name="jmeter.result.html.dir" value="${workspace}" />
  
  <!-- 生成的报告的前缀-->  
  <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" />

  <!-- 添加钉钉通知相关属性 -->
  <property name="python.script.dir" value="D:\testtest" />
  <property name="dingtalk.script" value="${python.script.dir}\dingtalk_reporter.py" />
  <!-- 更新报告URL基础地址 -->
  <property name="report.url.base" value="http://192.168.3.166:8080/job/test-jm/HTML_20Report/" />

  <!-- 初始化目标:创建目录 -->
  <target name="init">
    <echo message="创建输出目录..." />
    <mkdir dir="${jmeter.result.jtl.dir}" />
    <mkdir dir="${jmeter.result.html.dir}" />
    <echo message="输出目录已创建: ${jmeter.result.jtl.dir}" />
  </target>

  <!-- 使用antcall确保每个目标独立执行 -->
  <target name="run">
    <antcall target="init" />
    <antcall target="test" />
    <antcall target="report" />
    <antcall target="verify-files" />
    <antcall target="notify-dingtalk" />
    <echo message="JMeter测试流程已完成" />
  </target>

  <target name="test">
    <echo message="开始执行JMeter测试..." />
    <echo message="JMX文件目录: ${workspace1}" />
    <taskdef name="jmeter" 
             classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask" />
    <jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
      <!-- 声明要运行的脚本"*.jmx"指包含此目录下的所有jmeter脚本 -->
      <testplans dir="${workspace1}" 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.url" value="true"/>
      <property name="jmeter.save.saveservice.responseHeaders" value="true"/>
    </jmeter>
    <echo message="JMeter测试执行完成,结果保存在: ${jmeter.result.jtlName}" />
  </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">
    <echo message="开始生成HTML报告..." />
    <echo message="JTL文件路径: ${jmeter.result.jtlName}" />
    <echo message="HTML文件路径: ${jmeter.result.htmlName}" />
    
    <!-- 检查JTL文件是否存在   jmeter.results.shanhe.me jmeter-results-report_21  -->
    <available file="${jmeter.result.jtlName}" property="jtl.file.exists"/>
    <fail unless="jtl.file.exists" message="JTL文件不存在: ${jmeter.result.jtlName}" />
    
    <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="showData" expression="y"/>
          <param name="titleReport" expression="${report.title}"/>
          <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>
    <echo message="HTML报告生成完成,保存在: ${jmeter.result.htmlName}" />
  </target>

  <!-- 验证文件是否存在 -->
  <target name="verify-files">
    <echo message="验证文件是否在正确位置..." />
    <echo message="工作空间: ${workspace}" />
    
    <available file="${jmeter.result.jtlName}" property="jtl.exists"/>
    <available file="${jmeter.result.htmlName}" property="html.exists"/>
    
    <condition property="files.exist">
        <and>
            <isset property="jtl.exists"/>
            <isset property="html.exists"/>
        </and>
    </condition>
    
    <fail unless="files.exist" message="重要文件不存在,请检查路径配置" />
    <echo message="所有文件都在正确位置" />
    
    <!-- 检查文件大小 -->
    <length file="${jmeter.result.jtlName}" property="jtl.file.size"/>
    <length file="${jmeter.result.htmlName}" property="html.file.size"/>
    <!--echo message="JTL文件大小: ${jtl.file.size} 字节" / -->
    <!--echo message="HTML文件大小: ${html.file.size} 字节" / -->
    
    <!-- 列出testxml目录中的所有文件 -->
    <!-- echo message="testxml目录中的文件:" / -->
    <fileset id="testxml.files" dir="${jmeter.result.html.dir}" includes="*" />
    <pathconvert pathsep="${line.separator}   - " property="testxml.files.list" refid="testxml.files" />
    <!-- echo message="   - ${testxml.files.list}" / -->
  </target>

  <!-- 钉钉通知目标 -->
  <target name="notify-dingtalk">
    <echo message="开始发送钉钉通知..." />
    <echo message="JTL文件: ${jmeter.result.jtlName}" />
    <echo message="报告URL: ${report.url.base}${ReportName}${time}.html" />
    
    <exec executable="python" failonerror="false">
      <arg value="${dingtalk.script}"/>
      <arg value="${jmeter.result.jtlName}"/>
      <!-- 确保报告URL正确拼接 -->
      <arg value="${report.url.base}${ReportName}${time}.html"/>
    </exec>
    <echo message="钉钉通知发送完成" />
  </target>

</project>

六、python钉钉格式化发送脚本

钉钉消息发送python脚本dingtalk_reporter.py:

# coding=utf-8
import json
import os
import sys
import requests
from datetime import datetime
import xml.etree.ElementTree as ET
import logging

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# 配置项 - 在这里设置@策略
CONFIG = {
    # @策略: 'all'=@所有人, 'specific'=@特定人员, 'none'=不@
    "at_strategy": "all",
    
    # 特定人员的手机号列表(当at_strategy为'specific'时生效)
    "at_mobiles": ["15847395671"],
    
    # 钉钉Webhook URL
    "webhook_url": "https://oapi.dingtalk.com/robot/send?access_token=8ec5f1400b2c9358b1a762eaacdd83ade6f2f15278b4abd7bfb3458cb2349ec7"
}


def send_dingtalk_message(message, is_at_all=False, at_mobiles=[]):
    """
    发送钉钉消息

    Args:
        message (str): 要发送的消息内容(Markdown格式)
        is_at_all (bool): 是否@所有人
        at_mobiles (list): 要@的手机号列表

    Returns:
        bool: 发送成功返回True,否则返回False
    """
    headers = {'Content-Type': 'application/json'}

    # 构建完整的消息数据
    data = {
        "msgtype": "markdown",
        "markdown": {
            "title": "JMeter接口测试报告",
            "text": message
        },
        "at": {
            "atMobiles": at_mobiles,
            "isAtAll": is_at_all
        }
    }

    #logger.info(f"发送消息配置: is_at_all={is_at_all}, at_mobiles={at_mobiles}")

    try:
        logger.info("正在发送钉钉消息...")
        response = requests.post(CONFIG["webhook_url"], headers=headers, data=json.dumps(data), timeout=10)
        result = response.json()
        
        if result.get('errcode') == 0:
            logger.info("钉钉消息发送成功")
            return True
        else:
            logger.error(f"钉钉消息发送失败: {result.get('errmsg')}")
            return False
    except Exception as e:
        logger.error(f"发送钉钉消息时出错: {e}")
        return False


def parse_jmeter_results(jtl_file_path, html_report_url):
    """
    解析JMeter JTL结果文件

    Args:
        jtl_file_path (str): JTL文件路径
        html_report_url (str): HTML报告URL

    Returns:
        tuple: (格式化后的消息内容, 失败用例数, 错误用例数)
    """
    try:
        logger.info(f"开始解析JTL文件: {jtl_file_path}")
        
        # 检查文件是否存在
        if not os.path.exists(jtl_file_path):
            return f"## 测试执行失败  \nJTL结果文件不存在: {jtl_file_path}", 0, 0

        # 解析JTL文件获取详细测试数据
        tree = ET.parse(jtl_file_path)
        root = tree.getroot()

        total_samples = 0
        failed_samples = 0
        error_samples = 0
        total_time = 0.0
        success_count = 0

        # 提取测试结果数据
        for sample in root.findall('.//httpSample'):
            total_samples += 1
            total_time += float(sample.get('t', 0))

            # 检查请求是否成功
            success = sample.get('s', 'false') == 'true'
            if success:
                success_count += 1
            else:
                failed_samples += 1

            # 检查断言失败
            assertion_result = sample.find('assertionResult')
            if assertion_result is not None:
                failure = assertion_result.find('failure')
                error = assertion_result.find('error')
                if (failure is not None and failure.text == 'true') or \
                   (error is not None and error.text == 'true'):
                    error_samples += 1

        # 计算成功率
        success_rate = (success_count / total_samples * 100) if total_samples > 0 else 0
        avg_response_time = total_time / total_samples if total_samples > 0 else 0

        logger.info(f"解析完成: 总用例数={total_samples}, 成功={success_count}, 失败={failed_samples}, 错误={error_samples}")

        # 构建Markdown格式的消息 **平均响应时间**: {avg_response_time:.2f} ms  \n    **错误用例数**: {error_samples}  \n
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        message = f"""## 管控接口测试报告通知  \n
**构建时间**: {current_time}  \n
**总用例数**: {total_samples}  \n
**成功用例数**: {success_count}  \n
**失败用例数**: {failed_samples}  \n
**成功率**: {success_rate:.2f}%  \n
### [点击查看详细测试报告]({html_report_url})  \n
{"❌ **存在测试失败,请及时检查!**  \n" if failed_samples > 0 or error_samples > 0 else "✅ **所有测试通过!**  \n"}
"""
        return message, failed_samples, error_samples
        
    except ET.ParseError as e:
        error_msg = f"解析XML文件时出错: {str(e)}"
        logger.error(error_msg)
        return error_msg, 0, 0
    except Exception as e:
        error_msg = f"解析测试结果时发生未知错误: {str(e)}"
        logger.error(error_msg)
        return error_msg, 0, 0


def get_at_config():
    """
    根据配置返回@设置
    Returns:
        tuple: (is_at_all, at_mobiles)
    """
    strategy = CONFIG["at_strategy"]
    
    if strategy == "all":
        return True, []
    elif strategy == "specific":
        return False, CONFIG["at_mobiles"]
    elif strategy == "none":
        return False, []
    else:
        logger.warning(f"未知的@策略: {strategy}, 使用默认策略: 不@")
        return False, []


if __name__ == "__main__":
    if len(sys.argv) < 3:
        error_msg = "用法: python dingtalk_reporter.py <jtl文件路径> <报告URL>"
        logger.error(error_msg)
        print(error_msg)
        sys.exit(1)

    jtl_path = sys.argv[1]
    html_report_url = sys.argv[2]

    # 支持命令行参数覆盖配置
    if len(sys.argv) >= 4:
        CONFIG["at_strategy"] = sys.argv[3]  # 第四个参数设置@策略
    
    if len(sys.argv) >= 5:
        # 第五个参数可以设置手机号,多个手机号用逗号分隔
        CONFIG["at_mobiles"] = sys.argv[4].split(',')

    #logger.info(f"当前配置: 策略={CONFIG['at_strategy']}, 手机号={CONFIG['at_mobiles']}")

    # 解析结果并发送消息
    report_message, failed_count, error_count = parse_jmeter_results(jtl_path, html_report_url)
    
    logger.info(f"解析结果: 失败={failed_count}, 错误={error_count}")

    # 决定是否@
    is_at_all = False
    at_mobiles = []
    
    if failed_count > 0 or error_count > 0:
        # 测试失败时根据配置进行@
        is_at_all, at_mobiles = get_at_config()
        logger.info(f"测试存在失败,@配置: is_at_all={is_at_all}, at_mobiles={at_mobiles}")
    else:
        logger.info("测试通过,不@任何人")

    # 发送钉钉消息
    success = send_dingtalk_message(report_message, is_at_all, at_mobiles)

    # 根据发送结果退出
    if success:
        logger.info("脚本执行成功")
        sys.exit(0)
    else:
        logger.error("脚本执行失败")
        sys.exit(1)

七、手动触发执行脚本生成报告发送钉钉消息

3、在jmeter目录下创建test文件夹,把自己的jmx接口脚本、dingtalk_reporter.py、build.xml放入其中
注意把dingtalk_reporter.py、build.xml中的路径和地址按实际情况配置
在这里插入图片描述
4、验证dos窗口 进入输入ant -buildfile build.xml 按回车执行build文件出现如下内容证明成功:
在这里插入图片描述
这时候在jmeter新建的test文件下就会生成对应的HTML
在这里插入图片描述
钉钉会收到对应的执行结果,有失败的case时会@所有人

在这里插入图片描述

八、安装jenkins

1、进入官网:https://jenkins.io/zh/download/ 下载对应平台的安装包
初学者请选择长期支持版本,这个稳定的版本。 (直接百度云盘下载也可以)
如:你的操作系统是 windows,就选择对应的 windows 版本。

在这里插入图片描述
接下来配置 Jenkins 首先会提示你输入密钥(对应去文件夹里面寻找)
在这里插入图片描述
在这里插入图片描述
新手入门过程会很慢,且加载的东西会有失败的,有些插件加载失败没事,别慌,后面还可点击重试
在这里插入图片描述
创建用户名和密码,保存并完成。自己记住哈
在这里插入图片描述
在这里插入图片描述
配置完成,重启
在这里插入图片描述
重启完成进行登录,输入刚刚设置的账号密码
在这里插入图片描述
接下来创建一个新任务
在这里插入图片描述
输入任务名称,对应选择下面选项
在这里插入图片描述
选择构建环境,增加构建步骤,构建选择 Invoke Ant
在这里插入图片描述
选择高级
在这里插入图片描述
填写ant信息
在这里插入图片描述
保存成功,面板点击构建
在这里插入图片描述
在这里插入图片描述
Jenkins设置定时任务
如何设置定时任务,即让脚本每天每个小时,或者按照要求设定它的执行频率和时间?
首先进入test01设置
在这里插入图片描述
构建触发器,build 两分钟执行一次
在这里插入图片描述
在这里插入图片描述

九、Jenkins连接钉钉机器人发送构建消息

3、进入系统设置,下载钉钉插件
在这里插入图片描述
搜索dingding插件
在这里插入图片描述
安装完成查看
在这里插入图片描述
4、安装完成记得重启Jenkins程序
下载没有问题,进入Jenkins系统设置
在这里插入图片描述
在这里插入图片描述
回到登录账号里面填写手机号码
在这里插入图片描述
5、进入新建的项目里面,设置钉钉机器人
在这里插入图片描述
点击构建,见证时刻
在这里插入图片描述
在这里插入图片描述

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐