jmeter+ant+jenkins+python接口自动化框架

背景

部分websocket请求+http请求,需要实现巡检自动报警,使用jmeter+ant+python+jenkins实现接口报错后通过python解析jmeter运行结果文件,有报错自动发送消息到企业微信,发送内容包括报错信息、用例名称,无报错不发送

 

websocket流程图

 

 

ant配置

  1. 将jmeter安装目录下 extras文件内的ant-jmeter-1.1.1.jar 复制到 ant lib/ 目录下
  2. 在ant安装目录下新建build.xml文件,参考如下文件内容修改:
<?xml version="1.0" encoding="UTF-8"?>
 
<project name="ant-jmeter-test" default="run" basedir=".">
    <tstamp>
        <format property="time" pattern="yyyy-MM-dd HH-mm" />
    </tstamp>
    <!-- 需要改成自己本地的 Jmeter 目录--> 
    <property name="jmeter.home" value="D:\apache-jmeter-4.0" />
    <!-- jmeter生成jtl格式的结果报告的路径-->
    <property name="jmeter.result.jtl.dir" value="D:\result-jtl" />
    <!-- jmeter生成html格式的结果报告的路径-->
    <property name="jmeter.result.html.dir" value="D:\result-report" />
    <!-- 生成的报告的前缀--> 
    <property name="ReportName" value="TestReport-" />
    <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" />
     
    <path id="xslt.classpath">
        <fileset dir="${jmeter.home}/lib" includes="xalan*.jar"/>
        <fileset dir="${jmeter.home}/lib" includes="serializer*.jar"/>
    </path>
     
    <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="" includes="*.jmx" />
             
        </jmeter>
    </target>
         
    <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>

 

jmeter配置

  1. 编辑jmeter jmeter.properties文件,把jmeter.save.saveservice.output_format=csv 值修改为xml,即:jmeter.save.saveservice.output_format=xml

  2. jmeter.properties文件中,修改如下值为true

jmeter.save.saveservice.data_type=true
jmeter.save.saveservice.label=true
jmeter.save.saveservice.response_code=true
# response_data is not currently supported for CSV output
jmeter.save.saveservice.response_data=true
# Save ResponseData for failed samples
jmeter.save.saveservice.response_data.on_error=true
jmeter.save.saveservice.response_message=true
jmeter.save.saveservice.successful=true
jmeter.save.saveservice.thread_name=true
jmeter.save.saveservice.time=true
jmeter.save.saveservice.subresults=true
jmeter.save.saveservice.assertions=true
jmeter.save.saveservice.latency=true
jmeter.save.saveservice.connect_time=true
jmeter.save.saveservice.samplerData=true
jmeter.save.saveservice.responseHeaders=true
jmeter.save.saveservice.requestHeaders=true
jmeter.save.saveservice.encoding=true
jmeter.save.saveservice.bytes=true
# Only available with HttpClient4
jmeter.save.saveservice.sent_bytes=true
jmeter.save.saveservice.url=true
jmeter.save.saveservice.filename=true
jmeter.save.saveservice.hostname=true
jmeter.save.saveservice.thread_counts=true
jmeter.save.saveservice.sample_count=true
jmeter.save.saveservice.idle_time=true

 

jenkins安装插件

  • Ant In Workspace
  • Groovy Postbuild
  • HTML Publisher
  • Localization: Chinese (Simplified)

 

默认构建

 

指定构建文件

 

报告样式

  • System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")

 

html报告配置

 

自动发送报错信息python脚本

# coding=utf-8

from xml.dom.minidom import parse
import xml.dom.minidom
import os
import time
import requests
import json

def get_jtl():
    jtl_path = '/home/run_jmx_online/jtl/'
    dir_list = os.listdir(jtl_path)
    if not dir_list:
        return
    else:
        dir_list = sorted(dir_list,key=lambda x: os.path.getmtime(os.path.join(jtl_path, x)))
        print(' jtl文件列表: ' + str(dir_list))
        jtl_file = jtl_path + ''.join(dir_list[-1])
        print(' 读取文件: ' + jtl_file)
        return jtl_file

def read_xml():
    error_msg_list = []
    dom_tree = xml.dom.minidom.parse(get_jtl())
    collection = dom_tree.documentElement
    httpSample = collection.getElementsByTagName('httpSample')
    sample = collection.getElementsByTagName('sample')

    judge = {'true' : 0, 'error' : 0}
    for each_httpSample in httpSample:
        if each_httpSample.hasAttribute('s'):
            if each_httpSample.getAttribute('s') == 'true':
                judge['true'] += 1

            if each_httpSample.getAttribute('s') == 'false' and each_httpSample.getAttribute('rc') != '401' and each_httpSample.getAttribute('rm') != 'Sampler configured for using existing connection, but there is no connection':

                judge['error'] += 1
                httpSample_error_title = each_httpSample.getAttribute('lb')
                httpSample_error_failure_message = each_httpSample.getElementsByTagName('failureMessage')[0].childNodes[0].nodeValue
                httpSample_error_response_data = each_httpSample.getElementsByTagName('responseData')[0].childNodes[0].nodeValue
                error_msg_list.append({'请求标题' : httpSample_error_title,
                                       '断言失败信息' : httpSample_error_failure_message,
                                       '实际返回信息' : httpSample_error_response_data})

    for each_sample in sample:
        if each_sample.hasAttribute('s'):
            if each_sample.getAttribute('s') == 'true':
                judge['true'] += 1

            if each_sample.getAttribute('s') == 'false' and each_sample.getAttribute('rc') != '401' and each_sample.getAttribute('rm') != 'Sampler configured for using existing connection, but there is no connection' and each_sample.getAttribute('rc') != 'Websocket I/O error':

                judge['error'] += 1
                sample_error_title = each_sample.getAttribute('lb')
                # sample_error_failure_message = each_sample.getElementsByTagName('failureMessage')[0].childNodes[0].nodeValue
                sample_error_response_data = each_sample.getElementsByTagName('responseData')[0].childNodes[0].nodeValue
                error_msg_list.append({'请求标题' : sample_error_title,
                                       # '断言失败信息' : sample_error_failure_message,
                                       '实际返回信息' : sample_error_response_data})

    print('正确请求数: ' + str(judge['true']))
    print('错误请求数: ' + str(judge['error']))
    print('错误请求: ' + str(error_msg_list))
    return {'judge' : judge, 'error_msg_list' : error_msg_list}

def result_inform():
    now_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))

    if read_xml()['judge']['error'] == 0:
        pass
    else:
        result_true = read_xml()['judge']['true']
        result_error = read_xml()['judge']['error']
        all_result = result_true + result_error

        error_msg_list = read_xml()['error_msg_list']
        url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send'
        headers = {"Content-Type": "text/plain"}

        data = {
            "msgtype": "text",
            "text": {
                "content": now_time + '\n\n' +
                           '线上环境\n\n' +
                           '请求总数: ' + str(all_result) + '\n\n' +
                           '正确请求数: ' + str(result_true) + '\n\n' +
                           '错误请求数: ' + str(result_error) + '\n\n' +
                           '错误请求信息: ' + str(error_msg_list) + '\n\n' +
                           '内网测试报告: ' + 'http:///job/ims_test_report_online/ims-test-report-online/' + '\n'
            }
        }
        r = requests.post(url, headers=headers, json=data)

        print('发送信息:{}'.format(data))



if __name__ == '__main__':
    result_inform()

 

删除历史报告文件脚本

  • 每次都会保留最近三次的报告文件,其他的删除
import os


def del_jtl(path):
    dir_list = os.listdir(path)
    if not dir_list:
        return
    else:
        dir_list = sorted(dir_list,key=lambda x: os.path.getmtime(os.path.join(path, x)))
        del_jtl = dir_list[0:-3]
        print(' jtl 文件列表: ' + str(dir_list))
        # print(del_jtl)
        for i in del_jtl:
            os.remove(path + ''.join(i))
            print('删除 jtl 文件: ' + path + ''.join(i))

def del_html(path):
    dir_list = os.listdir(path)
    if not dir_list:
        return
    else:
        dir_list = sorted(dir_list,key=lambda x: os.path.getmtime(os.path.join(path, x)))
        del_jtl = dir_list[3:-3]
        print(' html 文件列表: ' + str(dir_list))
        print(del_jtl)
        for i in del_jtl:
            os.remove(path + ''.join(i))
            print('删除 html 文件: ' + path + ''.join(i))


del_jtl('/home/run_jmx_online/jtl/')
del_html('/home/run_jmx_online/html/')

del_jtl('/home/run_jmx_standby/jtl/')
del_html('/home/run_jmx_standby/html/')

 

报告内容

 

posted @ 2022-02-10 16:28  Echo丶Mikasa  阅读(420)  评论(0)    收藏  举报