基于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 cookielib 登录人人网的实现代码
Dec 19 Python
Python标准库urllib2的一些使用细节总结
Mar 16 Python
python类和继承用法实例
Jul 07 Python
python比较两个列表大小的方法
Jul 11 Python
Python模糊查询本地文件夹去除文件后缀的实例(7行代码)
Nov 09 Python
Python内建模块struct实例详解
Feb 02 Python
用Python将mysql数据导出成json的方法
Aug 21 Python
python 求一个列表中所有元素的乘积实例
Jun 11 Python
Python中的pathlib.Path为什么不继承str详解
Jun 23 Python
python3中利用filter函数输出小于某个数的所有回文数实例
Nov 24 Python
python 实现查询Neo4j多节点的多层关系
Dec 23 Python
如何使用 Python 读取文件和照片的创建日期
Sep 05 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
session 的生命周期是多长
2006/10/09 PHP
同时提取多条新闻中的文本一例
2006/10/09 PHP
解析php中curl_multi的应用
2013/07/17 PHP
curl不使用文件存取cookie php使用curl获取cookie示例
2014/01/26 PHP
PHP Opcache安装和配置方法介绍
2015/05/28 PHP
PHP获取数组中单列值的方法
2017/06/10 PHP
jQuery预加载图片常用方法
2015/06/15 Javascript
js下拉选择框与输入框联动实现添加选中值到输入框的方法
2015/08/17 Javascript
JavaScript遍历求解数独问题的主要思路小结
2016/06/12 Javascript
常用原生js自定义函数总结
2016/11/20 Javascript
Bootstrap基本布局实现方法详解
2016/11/25 Javascript
vue.js之vue-cli脚手架的搭建详解
2017/05/05 Javascript
jquery图片放大镜效果
2017/06/23 jQuery
jQuery实现遍历XML节点和属性的方法示例
2018/04/29 jQuery
JS基于对象的链表实现与使用方法示例
2019/01/31 Javascript
vue 使用 vue-pdf 实现pdf在线预览的示例代码
2020/04/26 Javascript
JS中的变量作用域(console版)
2020/07/18 Javascript
JavaScript中的Proxy对象
2020/11/27 Javascript
wxPython学习之主框架实例
2014/09/28 Python
全面了解Python的getattr(),setattr(),delattr(),hasattr()
2016/06/14 Python
深入理解Python对Json的解析
2017/02/14 Python
django与小程序实现登录验证功能的示例代码
2019/02/19 Python
django数据模型on_delete, db_constraint的使用详解
2019/12/24 Python
python如何删除文件、目录
2020/06/23 Python
Python是怎样处理json模块的
2020/07/16 Python
CSS3 clip-path 用法介绍详解
2018/03/01 HTML / CSS
用CSS3实现无限循环的无缝滚动的实例代码
2017/07/04 HTML / CSS
埃弗顿足球俱乐部官方网上商店:Everton Direct
2018/01/13 全球购物
Fabletics官网:美国运动服饰品牌,由好莱坞女演员凯特·哈德森创立
2019/10/19 全球购物
Linux中如何设置Java环境变量(Ubuntu)
2016/07/24 面试题
中英双版中文教师求职信
2013/10/27 职场文书
门诊挂号室室长岗位职责
2013/11/27 职场文书
物业公司采购员岗位职责
2013/12/31 职场文书
社区母亲节活动记录
2014/03/06 职场文书
后备干部推荐材料
2014/12/24 职场文书
Redis 配置文件重要属性的具体使用
2021/05/20 Redis