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

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运行及生成报告的命令如下。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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中,如果一个组件有子组件,格式为

1

2

3

4

5

6

7

8

9

10

11

12

13

<ThreadGroup 组件基本属性>

   ...线程组配置

</ThreadGroup>

<hashTree>

   ...内部子组件

</hashTree>

···

如果不包含子组件,则后面接一个<hashTree/> 单标签直接结束,例如:

```xml

<CSVDataSet>

  ... CSV数据组件配置

</CSVDataSet>

<hashTree/>

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

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

Jinja2中文文档

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

1

2

3

4

5

6

7

8

9

10

11

12

13

测试计划

  DNS缓存管理器

  Cookies管理器

  CSV文件(多个)

  ...

  聚合报告

  线程组(多个)

    CSV文件(多个)

    HTTP请求(或Dubbo请求)

      HTTP请求头管理器

      CSV文件(多个)

      响应断言

      察看结果树

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

data.yaml

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

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代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

<?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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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完成,压测节点数据分发的服务管理。

 

 感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 

          视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片即可自行领取。

### 关于JMeter压力测试脚本的示例与教程 #### JMeter简介 Apache JMeter 是一款由 Apache 组织开发的开源工具,主要用于性能测试和功能测试。它支持多种协议,包括 HTTP、HTTPS、FTP 和数据库查询等[^2]。 #### 创建简单的JMeter压力测试脚本 以下是创建运行一个基本的压力测试脚本的过程: 1. **下载启动JMeter** 用户可以通过访问官网下载最新版本的 JMeter 解压缩到本地环境。启动方式有多种方法可以选择,比如通过双击 `jmeter.bat` 文件或者使用命令行输入 `java -jar ApacheJmeter.jar` 来启动程序[^4]。 2. **配置线程组** 打开 JMeter 后,在测试计划中右键点击“添加 -> 线程(用户) -> 线程组”。设置线程数(即发用户数量)、Ramp-Up 时间以及循环次数。这些参数决定了模拟用户的负载情况[^2]。 3. **添加HTTP请求采样器** 右键点击刚刚创建好的线程组,“添加 -> 采样器(Sampler) -> HTTP 请求”,填写目标服务器地址及相关路径信息。这一步定义了要发送给服务端的具体请求内容[^5]。 4. **查看结果监听器** 添加“察看结果树”组件可以帮助开发者观察每次请求的结果详情;但在实际大规模压测过程中应考虑将其禁用以减少不必要的资源占用[^3]。 5. **保存与执行脚本** 完成上述配置之后可以保存整个项目文件(.jmx),通过如下命令行形式运行非GUI模式下的自动化测试任务: ```bash jmeter -n -t /path/to/your/testplan.jmx -l /path/to/resultsfile.jtl -e -o /path/to/htmlreport/ ``` 此外还可以利用 `-X` 参数指定额外属性覆盖默认行为[^1]。 6. **分析报告数据** 测试完成后生成的日志文件(result.jtl)能够进一步转化为HTML格式便于阅读理解。具体操作已在前面提到过如何转换为可视化的报表输出[^1]。 ```python import os def run_jmeter_test(test_plan_path, result_log_path, html_report_dir): command = f"jmeter -n -t {test_plan_path} -l {result_log_path} -e -o {html_report_dir}" os.system(command) if __name__ == "__main__": test_plan = "/usr/local/software/jmeter/temp/linux_users_api.jmx" log_file = "/usr/local/software/jmeter/temp/jtl/result.jtl" report_directory = "/usr/local/software/jmeter/temp/result/" run_jmeter_test(test_plan, log_file, report_directory) ``` 以上代码片段展示了怎样调用系统命令去批量处理多个不同的测试场景,最终得到结构化良好的 HTML 报告用于后续评估优化工作流效率等方面的表现指标变化趋势图等等有用的信息展示出来供相关人员参考决策依据之一部分而已。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值