基于Python组装jmx并调用JMeter实现压力测试


Posted in Python onNovember 03, 2020

JMeter可以通过os命令调用Python脚本,Python同样可以通过系统命令调用JMeter执行压测

Python调用JMeter

首先要安装JMeter,官方下载地址

解压并配置配置环境路径或建立软连,使得在命令输入jmeter便可以执行,如

unzip apache-jmeter-5.3.zip
mv apache-jmeter-5.3 /usr/loca/jmeter
ln -s /usr/local/jmeter/bin/jmeter /usr/bin/jmeter
ln -s /usr/local/jmeter/bin/jmeter-server /usr/bin/jmeter-server

打开JMeter并设计一个测试计划保存为testplan.jmx

使用Python调用JMeter压测并生成报告

Python中可以使用os.system()或supprocess.Popen()调用系统命令,前者实时显示在屏幕上,后者可以获取到屏幕输出信息。
使用Python调用JMeter运行及生成报告的命令如下。

import subprocess

jmx_file = 'testplan.jmx' # jmx文件路径
result_file = 'result.jtl' # 
log_file = 'run.log'
report_dir = 'report'

run_cmd = f'jmeter -n -t {jmx_file} -l {result_file} -j {log_file}' # 无界面运行JMeter压测命令
report_cmd = f'jmeter -g {result_file} -o {report_dir}' # 生成HTML报告命令

# 不需要获取屏幕输出是,可以使用os.system()
# os.system(run_cmd)
# os.system(report_cmd)

# 需要获取屏幕输出是,可以使用subprocess.Popen()
p1 = subprocess.Popen(run_cmd, shell=True, stdout=subprocess.PIPE)
print(p1.stdout.read().decode('utf-8'))
p2 = subprocess.Popen(report_cmd, shell=True, stdout=subprocess.PIPE)
print(p2.stdout.read().decode('utf-8'))

组装jmx

每一测试计划为一个jmx文件,jmx实际上是xml格式的,包含一些JMeter自定义的格式规范。
常用的组件有:

  • : 测试计划
  • : 线程组
  • : CSV数据文件
  • : HTTP请求
  • : HTTP请求头管理器
  • : Cookies管理器
  • : DNS缓存管理器
  • : 监听器(包括查看结果树、聚合报告等)
  • : 响应断言
  • <io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample></io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample>: 三方Dubbo请求插件

Dubbo插件jmeter-plugins-dubbo下载链接

jmx中,如果一个组件有子组件,格式为

<ThreadGroup 组件基本属性>
   ...线程组配置
</ThreadGroup>
<hashTree>
   ...内部子组件
</hashTree>
···
如果不包含子组件,则后面接一个<hashTree/> 单标签直接结束,例如:
```xml
<CSVDataSet>
  ... CSV数据组件配置
</CSVDataSet>
<hashTree/>

详细的格式可以自己使用JMeter创建一个测试计划,使用文本文件打开jmx文件查看。

使用Python组装jmx文件的方式有两种,一种是固定模板的数据渲染,一种类似JMeter的逐个组件添加,第一种比较简单。
通过分析jmx文件中的变量,我们使用jinja2模板语法,将其中的变量进行参数化,对需要判断和循环的变量设置if和for循环。

Jinja2中文文档

假设我们的测试计划结构为:

测试计划
  DNS缓存管理器
  Cookies管理器
  CSV文件(多个)
  ...
  聚合报告
  线程组(多个)
    CSV文件(多个)
    HTTP请求(或Dubbo请求)
      HTTP请求头管理器
      CSV文件(多个)
      响应断言
      察看结果树

将jmx中的关键数据抽取并组合,我使用的完整数据格式如下:

data.yaml

test_plan_name: 测试计划
comments: 测试计划描述
hosts:
 - name: las.secoo.com
  address: 112.126.120.128
cookies:
 clear_each_iteration: 'true'
csv_files:
 - {'name': '数据文件1', 'path': 'data.csv', 'varnames': 'a,b', 'delimiter': ','}
 - {'name': '数据文件2', 'path': 'data.csv', 'varnames': 'c,d', 'delimiter': ','}
thread_groups:
 - thread_group_name: 线程组1
  comments: 线程组1描述
  enabled: 'true'
  num_threads: 50
  loops: -1
  ramp_time: 0
  scheduler: 'true'
  duration: 30
  delay: ''
  http_samples:
   - request_name: HTTP-GET
    enabled: 'true'
    csv_files:
     - {'name': '数据文件4', 'path': 'data.csv', 'varnames': 'a,b', 'delimiter': ','}
    request:
     protocol: https
     domain: httpbin.org
     port: ''
     encoding: ''
     path: /get
     method: GET
     connect_timeout: ''
     response_timeout: ''
     params: {'a': 1, 'b': 2}
     headers: {'token': 'aaaaaa', 'x-text': 'bbbbb'}
     follow_redirects: 'false'
     auto_redirects: 'false'
     use_keepalive: 'false'
    validate:
     - test_field: response_data # response_data响应文本 response_code响应代码response_message响应信息response_headers
      # 请求头request_headers sample_label URL样本 response_data_as_document文档(文本) 请求数据request_data
      test_type: 2 # 2 包括 1匹配 8 相等 16字符串 否+4 或者+32
      strings: ['a', 'b']
   - request_name: HTTP-POST
    enabled: 'true'
    request:
     protocol: https
     domain: httpbin.org
     port: ''
     encoding: ''
     path: /post
     method: POST
     data: {'c': 3, 'd': 4}
     follow_redirects: 'false'
     auto_redirects: 'false'
     use_keepalive: 'false'
     connect_timeout: ''
     response_timeout: ''
   - request_name: HTTP-JSON
    enabled: 'true'
    request:
     protocol: https
     domain: httpbin.org
     port: ''
     encoding: ''
     path: /post
     method: POST
     connect_timeout: ''
     response_timeout: ''
     raw_data: '{"e": 5, "f": 6}'
     follow_redirects: 'false'
     auto_redirects: 'false'
     use_keepalive: 'false'
 - thread_group_name: 线程组2
  comments: 线程组2描述
  enabled: 'false'
  num_threads: 50
  loops: -1
  ramp_time: 0
  scheduler: 'true'
  duration: 30
  delay: ''
  csv_files:
   - {'name': '数据文件3', 'path': 'data.csv', 'varnames': 'a,b','delimiter': '\t'}
  dubbo_samples:
   - request_name: 查询运费接口-dubbo
    enabled: 'true'
    registry:
     type: zookeeper
     group: ''
     address: 'zk-mall1.secoolocal.com:5181?backup=zk-mall2.secoolocal.com:5181,zk-mall3.secoolocal.com:5181'
    dubbo:
     timeout: 100
     retires: 0
     group: ''
     connections: 100
     load_balance: random
     cluster: failfast
     service: 'com.secoo.business.config.rpc.service.BusinessConfigStoreService'
     method: queryFreight
     headers:
      Content-Type: 'application/json'
     params:
      - type: java.util.List
       value: ${freight_wareHouseId_sendAreaId}
      - type: java.lang.String
       value: 110100
      - type: java.util.List
       value: ${freight_wareHouseId_sendAreaId}
    validate:
     - test_field: response_data # response_data响应文本 response_code响应代码response_message响应信息response_headers
      test_type: 16 # 2 包括 1匹配 8 相等 16字符串 否+4 或者+32
      strings: ['"code": 0']

对应的模板文件tpl.xml代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.3">
 <hashTree>
  <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="{{test_plan_name}}" enabled="true">
   <stringProp name="TestPlan.comments">{{comments}}</stringProp>
   <boolProp name="TestPlan.functional_mode">false</boolProp>
   <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
   <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
   <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
    <collectionProp name="Arguments.arguments"/>
   </elementProp>
   <stringProp name="TestPlan.user_define_classpath"></stringProp>
  </TestPlan>
  <hashTree>{% if hosts %}
   <DNSCacheManager guiclass="DNSCachePanel" testclass="DNSCacheManager" testname="DNS缓存管理器" enabled="true">
    <collectionProp name="DNSCacheManager.servers"/>
    <collectionProp name="DNSCacheManager.hosts">{% for host in hosts %}
     <elementProp name="las.secoo.com" elementType="StaticHost">
      <stringProp name="StaticHost.Name">{{host.name}}</stringProp>
      <stringProp name="StaticHost.Address">{{host.address}}</stringProp>
     </elementProp>{% endfor %}
    </collectionProp>
    <boolProp name="DNSCacheManager.clearEachIteration">false</boolProp>
    <boolProp name="DNSCacheManager.isCustomResolver">true</boolProp>
   </DNSCacheManager>
   <hashTree/>{% endif %} {% if cookies %}
   <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie管理器" enabled="true">
    <collectionProp name="CookieManager.cookies"/>
    <boolProp name="CookieManager.clearEachIteration">{{cookies.clear_each_iteration}}</boolProp>
   </CookieManager>
   <hashTree/>{% endif %} {% if csv_files %}{% for csv_file in csv_files %}
   <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="{{csv_file.name}}" enabled="true">
    <stringProp name="filename">dat/{{csv_file.path}}</stringProp>
    <stringProp name="fileEncoding">UTF-8</stringProp>
    <stringProp name="variableNames">{{csv_file.varnames}}</stringProp>
    <boolProp name="ignoreFirstLine">true</boolProp>
    <stringProp name="delimiter">{{csv_file.delimiter}}</stringProp>
    <boolProp name="quotedData">false</boolProp>
    <boolProp name="recycle">true</boolProp>
    <boolProp name="stopThread">false</boolProp>
    <stringProp name="shareMode">shareMode.group</stringProp>
   </CSVDataSet>
   <hashTree/>{% endfor %}{% endif %}
   <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="聚合报告" enabled="true">
    <boolProp name="ResultCollector.error_logging">false</boolProp>
    <objProp>
     <name>saveConfig</name>
     <value class="SampleSaveConfiguration">
      <time>true</time>
      <latency>true</latency>
      <timestamp>true</timestamp>
      <success>true</success>
      <label>true</label>
      <code>true</code>
      <message>true</message>
      <threadName>true</threadName>
      <dataType>true</dataType>
      <encoding>false</encoding>
      <assertions>true</assertions>
      <subresults>true</subresults>
      <responseData>false</responseData>
      <samplerData>false</samplerData>
      <xml>false</xml>
      <fieldNames>true</fieldNames>
      <responseHeaders>false</responseHeaders>
      <requestHeaders>false</requestHeaders>
      <responseDataOnError>true</responseDataOnError>
      <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
      <assertionsResultsToSave>0</assertionsResultsToSave>
      <bytes>true</bytes>
      <sentBytes>true</sentBytes>
      <threadCounts>true</threadCounts>
      <idleTime>true</idleTime>
     </value>
    </objProp>
    <stringProp name="filename"></stringProp>
   </ResultCollector>
   <hashTree/>{% for thread_group in thread_groups %}
   <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="{{thread_group.thread_group_name}}" enabled="{{thread_group.enabled}}">
    <stringProp name="TestPlan.comments">{{thread_group.comments}}</stringProp>
    <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
    <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true">
     <boolProp name="LoopController.continue_forever">false</boolProp>
     <intProp name="LoopController.loops">{{thread_group.loops}}</intProp>
    </elementProp>
    <stringProp name="ThreadGroup.num_threads">{{thread_group.num_threads}}</stringProp>
    <stringProp name="ThreadGroup.ramp_time">{{thread_group.ramp_time}}</stringProp>
    <boolProp name="ThreadGroup.scheduler">{{thread_group.scheduler}}</boolProp>
    <stringProp name="ThreadGroup.duration">{{thread_group.duration}}</stringProp>
    <stringProp name="ThreadGroup.delay">{{thread_group.delay}}</stringProp>
    <boolProp name="ThreadGroup.same_user_on_next_iteration">false</boolProp>
   </ThreadGroup>
   <hashTree>{% if thread_group.csv_files %}{% for csv_file in thread_group.csv_files %}
    <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="{{csv_file.name}}" enabled="true">
    <stringProp name="filename">dat/{{csv_file.path}}</stringProp>
    <stringProp name="fileEncoding">UTF-8</stringProp>
    <stringProp name="variableNames">{{csv_file.varnames}}</stringProp>
    <boolProp name="ignoreFirstLine">true</boolProp>
    <stringProp name="delimiter">{{csv_file.delimiter}}</stringProp>
    <boolProp name="quotedData">false</boolProp>
    <boolProp name="recycle">true</boolProp>
    <boolProp name="stopThread">false</boolProp>
    <stringProp name="shareMode">shareMode.group</stringProp>
   </CSVDataSet>
    <hashTree/>{% endfor %}{% endif %} {% if thread_group.http_samples %}{% for http_sample in thread_group.http_samples %}
    <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="{{http_sample.request_name}}" enabled="{{http_sample.enabled}}">
     <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
      <collectionProp name="Arguments.arguments">{% if http_sample.request.params %}{% for name, value in http_sample.request.params.items() %}
       <elementProp name="{{name}}" elementType="HTTPArgument">
        <boolProp name="HTTPArgument.always_encode">false</boolProp>
        <stringProp name="Argument.value">{{value}}</stringProp>
        <stringProp name="Argument.metadata">=</stringProp>
        <boolProp name="HTTPArgument.use_equals">true</boolProp>
        <stringProp name="Argument.name">{{name}}</stringProp>
       </elementProp>{% endfor %}{% endif %} {% if http_sample.request.data %}{% for name, value in http_sample.request.data.items() %}
       <elementProp name="{{name}}" elementType="HTTPArgument">
        <boolProp name="HTTPArgument.always_encode">false</boolProp>
        <stringProp name="Argument.value">{{value}}</stringProp>
        <stringProp name="Argument.metadata">=</stringProp>
        <boolProp name="HTTPArgument.use_equals">true</boolProp>
        <stringProp name="Argument.name">{{name}}</stringProp>
       </elementProp>{% endfor %}{% endif %} {% if http_sample.request.raw_data %}
       <elementProp name="" elementType="HTTPArgument">
        <boolProp name="HTTPArgument.always_encode">false</boolProp>
        <stringProp name="Argument.value">{{http_sample.request.raw_data}}</stringProp>
        <stringProp name="Argument.metadata">=</stringProp>
       </elementProp>{% endif %}
      </collectionProp>
     </elementProp>
     <stringProp name="HTTPSampler.domain">{{http_sample.request.domain}}</stringProp>
     <stringProp name="HTTPSampler.port">{{http_sample.request.port}}</stringProp>
     <stringProp name="HTTPSampler.protocol">{{http_sample.request.protocol}}</stringProp>
     <stringProp name="HTTPSampler.contentEncoding">{{http_sample.request.encoding}}</stringProp>
     <stringProp name="HTTPSampler.path">{{http_sample.request.path}}</stringProp>
     <stringProp name="HTTPSampler.method">{{http_sample.request.method}}</stringProp>
     <boolProp name="HTTPSampler.follow_redirects">{{http_sample.request.follow_redirects}}</boolProp>
     <boolProp name="HTTPSampler.auto_redirects">{{http_sample.request.auto_redirects}}</boolProp>
     <boolProp name="HTTPSampler.use_keepalive">{{http_sample.request.use_keepalive}}</boolProp>
     <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
     <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
     <stringProp name="HTTPSampler.connect_timeout">{{http_sample.request.connect_timeout}}</stringProp>
     <stringProp name="HTTPSampler.response_timeout">{{http_sample.request.response_timeout}}</stringProp>
    </HTTPSamplerProxy>
    <hashTree>{% if http_sample.request.headers %}
     <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息头管理器" enabled="true">
      <collectionProp name="HeaderManager.headers">{% for name, value in http_sample.request.headers.items() %}
       <elementProp name="" elementType="Header">
        <stringProp name="Header.name">{{name}}</stringProp>
        <stringProp name="Header.value">{{value}}</stringProp>
       </elementProp>{% endfor %}
      </collectionProp>
     </HeaderManager>
     <hashTree/>{% endif %} {% if http_sample.csv_files %}{% for csv_file in http_sample.csv_files %}
     <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV 数据文件设置" enabled="true">
      <stringProp name="delimiter">{{csv_file.delimiter}}</stringProp>
      <stringProp name="fileEncoding">UTF_8</stringProp>
      <stringProp name="filename">dat/{{csv_file.path}}</stringProp>
      <boolProp name="ignoreFirstLine">true</boolProp>
      <boolProp name="quotedData">false</boolProp>
      <boolProp name="recycle">true</boolProp>
      <stringProp name="shareMode">shareMode.group</stringProp>
      <boolProp name="stopThread">false</boolProp>
      <stringProp name="variableNames">{{csv_file.varnames}}</stringProp>
     </CSVDataSet>
     <hashTree/>{% endfor %}{% endif %} {% if http_sample.validate %}{% for assertion in http_sample.validate %}
     <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="响应断言" enabled="true">
      <collectionProp name="Asserion.test_strings">{% if assertion.strings %}{% for string in assertion.strings %}
       <stringProp name="97">{{string}}</stringProp>{% endfor %}{% endif %}
      </collectionProp>
      <stringProp name="Assertion.custom_message"></stringProp>
      <stringProp name="Assertion.test_field">Assertion.{{assertion.test_field}}</stringProp>
      <boolProp name="Assertion.assume_success">false</boolProp>
      <intProp name="Assertion.test_type">{{assertion.test_type}}</intProp>
     </ResponseAssertion>
     <hashTree/>{% endfor %}{% endif %}
     <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="察看结果树" enabled="true">
      <boolProp name="ResultCollector.error_logging">false</boolProp>
      <objProp>
       <name>saveConfig</name>
       <value class="SampleSaveConfiguration">
        <time>true</time>
        <latency>true</latency>
        <timestamp>true</timestamp>
        <success>true</success>
        <label>true</label>
        <code>true</code>
        <message>true</message>
        <threadName>true</threadName>
        <dataType>true</dataType>
        <encoding>false</encoding>
        <assertions>true</assertions>
        <subresults>true</subresults>
        <responseData>false</responseData>
        <samplerData>false</samplerData>
        <xml>false</xml>
        <fieldNames>true</fieldNames>
        <responseHeaders>false</responseHeaders>
        <requestHeaders>false</requestHeaders>
        <responseDataOnError>false</responseDataOnError>
        <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
        <assertionsResultsToSave>0</assertionsResultsToSave>
        <bytes>true</bytes>
        <sentBytes>true</sentBytes>
        <url>true</url>
        <threadCounts>true</threadCounts>
        <idleTime>true</idleTime>
        <connectTime>true</connectTime>
       </value>
      </objProp>
      <stringProp name="filename"></stringProp>
     </ResultCollector>
     <hashTree/>
    </hashTree>{% endfor %}{% endif %} {% if thread_group.dubbo_samples %} {% for dubbo_sample in thread_group.dubbo_samples %}
    <io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample guiclass="io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui" testclass="io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample" testname="{{dubbo_sample.request_name}}" enabled="{{dubbo_sample.enabled}}">
     <stringProp name="FIELD_DUBBO_REGISTRY_PROTOCOL">{{dubbo_sample.registry.type}}</stringProp>
     <stringProp name="FIELD_DUBBO_REGISTRY_GROUP">{{dubbo_sample.registry.group}}</stringProp>
     <stringProp name="FIELD_DUBBO_RPC_PROTOCOL">dubbo://</stringProp>
     <stringProp name="FIELD_DUBBO_ADDRESS">{{dubbo_sample.registry.address}}</stringProp>
     <stringProp name="FIELD_DUBBO_TIMEOUT">{{dubbo_sample.dubbo.timeout}}</stringProp>
     <stringProp name="FIELD_DUBBO_VERSION"></stringProp>
     <stringProp name="FIELD_DUBBO_RETRIES">{{dubbo_sample.dubbo.retries}}</stringProp>
     <stringProp name="FIELD_DUBBO_GROUP">{{dubbo_sample.dubbo.group}}</stringProp>
     <stringProp name="FIELD_DUBBO_CONNECTIONS">{{dubbo_sample.dubbo.connections}}</stringProp>
     <stringProp name="FIELD_DUBBO_LOADBALANCE">{{dubbo_sample.dubbo.load_balance}}</stringProp>
     <stringProp name="FIELD_DUBBO_ASYNC">sync</stringProp>
     <stringProp name="FIELD_DUBBO_CLUSTER">{{dubbo_sample.dubbo.cluster}}</stringProp>
     <stringProp name="FIELD_DUBBO_INTERFACE">{{dubbo_sample.dubbo.service}}</stringProp>
     <stringProp name="FIELD_DUBBO_METHOD">{{dubbo_sample.dubbo.method}}</stringProp>
     <intProp name="FIELD_DUBBO_METHOD_ARGS_SIZE">1</intProp>{% for param in dubbo_sample.dubbo.params %}
      <stringProp name="FIELD_DUBBO_METHOD_ARGS_PARAM_TYPE1">{{param.type}}</stringProp>
      <stringProp name="FIELD_DUBBO_METHOD_ARGS_PARAM_VALUE1">{{param.value}}</stringProp>{% endfor %}
     <intProp name="FIELD_DUBBO_ATTACHMENT_ARGS_SIZE">0</intProp>
     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_PROTOCOL"></stringProp>
     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_GROUP"></stringProp>
     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_NAMESPACE"></stringProp>
     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_USER_NAME"></stringProp>
     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_PASSWORD"></stringProp>
     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_ADDRESS"></stringProp>
     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_TIMEOUT"></stringProp>
     <stringProp name="FIELD_DUBBO_REGISTRY_USER_NAME"></stringProp>
     <stringProp name="FIELD_DUBBO_REGISTRY_PASSWORD"></stringProp>
     <stringProp name="FIELD_DUBBO_REGISTRY_TIMEOUT"></stringProp>
    </io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample>
    <hashTree>{% if dubbo_sample.dubbo.headers %}
     <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息头管理器" enabled="true">
      <collectionProp name="HeaderManager.headers">{% for name, value in dubbo_sample.dubbo.headers.items() %}
       <elementProp name="" elementType="Header">
        <stringProp name="Header.name">{{name}}</stringProp>
        <stringProp name="Header.value">{{value}}</stringProp>
       </elementProp>{% endfor %}
      </collectionProp>
     </HeaderManager>
     <hashTree/>{% endif %} {% if dubbo_sample.validate %} {% for assertion in dubbo_sample.validate %}
     <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="响应断言" enabled="true">
      <collectionProp name="Asserion.test_strings">{% if assertion.strings %}{% for string in assertion.strings %}
       <stringProp name="97">{{string}}</stringProp>{% endfor %}{% endif %}
      </collectionProp>
      <stringProp name="Assertion.custom_message"></stringProp>
      <stringProp name="Assertion.test_field">Assertion.{{assertion.test_field}}</stringProp>
      <boolProp name="Assertion.assume_success">false</boolProp>
      <intProp name="Assertion.test_type">{{assertion.test_type}}</intProp>
     </ResponseAssertion>{% endfor %} {% endif %}
     <hashTree/>{% endfor %}{% endif %} {% endfor %}
   </hashTree>
  </hashTree>
 </hashTree>
</jmeterTestPlan>

组装出类似data.yaml格式的数据,并使用jinja2渲染模板即可得到完整的jmx文件

pip install pyyaml jinja2

import yaml
import jinja2

# 组装或读取数据
with open('data.yaml', encoding='utf-8') as f:
  data = yaml.safe_load(f)

 # 读取模板
with open('tpl.xml', encoding='utf-8') as f:
  tpl = f.read()

# 渲染模板生成jmx
jmx = jinja2.Template(tpl).render(data)
with open(jmx_file, 'w', encoding='utf-8') as f:
  f.write(jmx)

后计

在实际项目中,还涉及数据文件的拷贝,节点环境的部署,脚本的分发,报告的下载等等,可以使用paramiko或者fabric或ansible完成,压测节点数据分发的服务管理。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中使用glob和rmtree删除目录子目录及所有文件的例子
Nov 21 Python
Python在图片中添加文字的两种方法
Apr 29 Python
遗传算法之Python实现代码
Oct 10 Python
selenium+python实现1688网站验证码图片的截取功能
Aug 14 Python
Python中创建二维数组
Oct 17 Python
python运行时强制刷新缓冲区的方法
Jan 14 Python
python使用PIL实现多张图片垂直合并
Jan 15 Python
Python实现微信好友的数据分析
Dec 16 Python
TensorFlow tensor的拼接实例
Jan 19 Python
python 日志 logging模块详细解析
Mar 31 Python
python中怎么表示空值
Jun 19 Python
详解python算法常用技巧与内置库
Oct 17 Python
Python os库常用操作代码汇总
Nov 03 #Python
如何基于Python爬虫爬取美团酒店信息
Nov 03 #Python
python import 上级目录的导入
Nov 03 #Python
详解Python利用configparser对配置文件进行读写操作
Nov 03 #Python
Python抓包并解析json爬虫的完整实例代码
Nov 03 #Python
python中not、and和or的优先级与详细用法介绍
Nov 03 #Python
如何基于Python按行合并两个txt
Nov 03 #Python
You might like
德劲1107的电路分析与打磨
2021/03/02 无线电
摩卡咖啡
2021/03/03 咖啡文化
php数组应用之比较两个时间的相减排序
2008/08/18 PHP
php 更新数据库中断的解决方法
2009/06/05 PHP
php数组查找函数in_array()、array_search()、array_key_exists()使用实例
2014/04/29 PHP
php 截取GBK文档某个位置开始的n个字符方法
2017/03/08 PHP
PHP 二维array转换json的实例讲解
2018/08/21 PHP
js带按钮的提示框可供选择示例代码
2013/09/17 Javascript
javascript:FF/Chrome与IE动态加载元素的区别说明
2014/01/26 Javascript
父页面显示遮罩层弹出半透明状态的dialog
2014/03/04 Javascript
采用自执行的匿名函数解决for循环使用闭包的问题
2014/09/11 Javascript
JavaScript设置body高度为浏览器高度的方法
2015/02/09 Javascript
原生js实现放大镜特效
2017/03/08 Javascript
Vue2递归组件实现树形菜单
2017/04/10 Javascript
jQuery中的$是什么意思及 $. 和 $().的区别
2018/04/20 jQuery
详解Axios统一错误处理与后置
2018/09/26 Javascript
Vue表单输入绑定的示例代码
2018/11/01 Javascript
详解Node.js 中使用 ECDSA 签名遇到的坑
2018/11/26 Javascript
微信小程序通过js实现瀑布流布局详解
2019/08/28 Javascript
vue自定义树状结构图的实现方法
2020/10/18 Javascript
js实现随机点名功能
2020/12/23 Javascript
[03:04]DOTA2英雄基础教程 影魔
2013/12/11 DOTA
mvc框架打造笔记之wsgi协议的优缺点以及接口实现
2018/08/01 Python
Python 调用 Outlook 发送邮件过程解析
2019/08/08 Python
python对execl 处理操作代码
2020/06/22 Python
python 制作python包,封装成可用模块教程
2020/07/13 Python
HTML5新特性 多线程(Worker SharedWorker)
2017/04/24 HTML / CSS
美国高级音响品牌:Master&Dynamic
2018/07/05 全球购物
德国Discount-Apotheke中文官网:DC德式康线上药房
2020/02/18 全球购物
自荐书封面下载
2013/11/29 职场文书
大专生毕业的自我评价
2014/02/06 职场文书
道德模范先进事迹
2014/02/14 职场文书
辞职信格式范文
2015/05/13 职场文书
惊涛骇浪观后感
2015/06/05 职场文书
解析laravel使用workerman用户交互、服务器交互
2021/04/28 PHP
详解MySQL的内连接和外连接
2023/05/08 MySQL