jmeter脚本生成基于rap2
基于 生成 脚本 JMeter
2023-09-14 09:10:50 时间
回顾
相信大家在写jmeter脚本的时候,大多都是对着接口文档进行复制粘贴,新建测试计划-新建线程组-新建http请求,动作比较繁琐··如果我们将接口文档的数据转化为jmeter不就是挺香的么?
jmeter脚本分析
jmeter脚本后缀是.jmx,直接用文本文件打开,发现jmx其实就是个xml文件···引用百度百科一句话,xml,一种可扩展标记语言,标准通用标记语言的子集,简称XML。是一种用于标记电子文件使其具有结构性的标记语言。之前接触的webService接口,就是传入xml格式的数据···
关于xml科普可看资料介绍:www.cnblogs.com/bjphp/p/859…
jmx文件生成
jmx文件生成,我这里用了囤货大佬的源码,在基础上整合了rap2解析后的数据结构。 开源项目地址:testerhome.com/topics/2477…
源码大概逻辑就是,先新建一个root节点,写死一些子节点的属性,将子节点挂载在节点下,子子孙孙···
有兴趣可以看看lxml这个库,这个库爬虫的时候也用到哦~
下图我这里改了一下,源码是不支持把body数据set进去。
有时候转换xml文件时,有概率会出现这个错误
lxml.etree._raiseSerialisationError
lxml.etree.SerialisationError: IO_ENCODER
解决方案:stackoverflow.com/questions/6…
生成文件展示:
完整代码:
# -*- coding: utf-8 -*-
import json
from lxml import etree
class CreateJmx(object):
@classmethod
def jmeter_test_plan(cls, root_xml):
"""
设置根节点
:param root_xml: 根节点xml
:return:
"""
JmeterTestPlan = root_xml
JmeterTestPlan.set('version', '1.2')
JmeterTestPlan.set('properties', '5.0')
JmeterTestPlan.set('jmeter', '5.1.1 r1855137')
return etree.SubElement(JmeterTestPlan, 'hashTree')
@classmethod
def test_plan(cls, parent_xml, plan_name):
"""
:param parent_xml: 父节点
:param plan_name: 测试计划名称
:return:
"""
testPlan = etree.SubElement(parent_xml, 'TestPlan')
testPlan.set("guiclass", "TestPlanGui")
testPlan.set("testclass", "TestPlan")
testPlan.set("testname", plan_name)
testPlan.set("enabled", "true")
stringProp = etree.SubElement(testPlan, "stringProp")
stringProp.set("name", "TestPlan.comments")
stringProp.text = ''
boolProp1 = etree.SubElement(testPlan, "boolProp")
boolProp1.set("name", "TestPlan.functional_mode")
boolProp1.text = "false"
boolProp2 = etree.SubElement(testPlan, "boolProp")
boolProp2.set("name", "TestPlan.tearDown_on_shutdown")
boolProp2.text = "false"
boolProp3 = etree.SubElement(testPlan, "boolProp")
boolProp3.set("name", "TestPlan.serialize_threadgroups")
boolProp3.text = "true"
elementProp = etree.SubElement(testPlan, "elementProp")
elementProp.set("name", "TestPlan.user_defined_variables")
elementProp.set("elementType", "Arguments")
elementProp.set("guiclass", "ArgumentsPanel")
elementProp.set("testclass", "Arguments")
elementProp.set("testname", "User Defined Variables")
elementProp.set("enabled", "true")
collectionProp = etree.SubElement(elementProp, "collectionProp")
collectionProp.set("name", "Arguments.arguments")
stringProp2 = etree.SubElement(testPlan, "stringProp")
stringProp2.set("name", "TestPlan.user_define_classpath")
stringProp2.text = ''
return etree.SubElement(parent_xml, 'hashTree')
@classmethod
def thread_group(cls, parent_xml):
"""
:param parent_xml: 父节点
:return:
"""
theadGroup = etree.SubElement(parent_xml, "ThreadGroup")
theadGroup.set("guiclass", "ThreadGroupGui")
theadGroup.set("testclass", "ThreadGroup")
theadGroup.set("testname", "Thread Group")
theadGroup.set("enabled", "true")
stringProp = etree.SubElement(theadGroup, "stringProp")
stringProp.set("name", "ThreadGroup.on_sample_error")
stringProp.text = "continue"
elementProp = etree.SubElement(theadGroup, "elementProp")
cls.common_api(elementProp,
{"name": "ThreadGroup.main_controller", "elementType": "LoopController",
"guiclass": "LoopControlPanel",
"testclass": "LoopController", "testname": "Loop Controller", "enabled": "true"})
boolProp = etree.SubElement(elementProp, "boolProp")
boolProp.set("name", "LoopController.continue_forever")
boolProp.text = "false"
stringProp = etree.SubElement(elementProp, "stringProp")
stringProp.set("name", "LoopController.loops")
stringProp.text = "1"
stringProp = etree.SubElement(theadGroup, "stringProp")
stringProp.set("name", "ThreadGroup.num_threads")
stringProp.text = "1"
stringProp = etree.SubElement(theadGroup, "stringProp")
stringProp.set("name", "ThreadGroup.ramp_time")
stringProp.text = "1"
boolProp = etree.SubElement(theadGroup, "boolProp")
boolProp.set("name", "ThreadGroup.scheduler")
boolProp.text = "false"
stringProp = etree.SubElement(theadGroup, "stringProp")
stringProp.set("name", "ThreadGroup.duration")
stringProp.text = ''
stringProp = etree.SubElement(theadGroup, "stringProp")
stringProp.set("name", "ThreadGroup.delay")
stringProp.text = ''
return etree.SubElement(parent_xml, "hashTree")
@classmethod
def arguments(cls, parent_xml):
"""
设置arguments
:param parent_xml: 父节点
:return:
"""
Arguments = etree.SubElement(parent_xml, "ThreadGroup")
Arguments.set("guiclass", "ArgumentsPanel")
Arguments.set("testclass", "Arguments")
Arguments.set("testname", "User defined variables")
Arguments.set("enabled", "true")
collectionProp = etree.SubElement(Arguments, "collectionProp")
collectionProp.set("name", "Arguments.arguments")
return etree.SubElement(parent_xml, "hashTree")
@classmethod
def controller(cls, parent_xml, result):
"""
创建HTTPsampler
:param parent_xml: 父节点
:param result: 接口数据,rap或者swagger
:return:
"""
for data in result:
GenericController = etree.SubElement(parent_xml, "GenericController")
GenericController.set("guiclass", "LogicControllerGui")
GenericController.set("testclass", "GenericController")
GenericController.set("testname", data.get("modules"))
GenericController.set("enabled", "true")
stringProp = etree.SubElement(GenericController, "stringProp")
stringProp.set("name", "TestPlan.comments")
stringProp.text = data.get("description")
shashTree = etree.SubElement(parent_xml, "hashTree")
for sample in data.get("cases_data"):
HTTPSamplerProxy = etree.SubElement(shashTree, "HTTPSamplerProxy")
cls.common_api(HTTPSamplerProxy,
{"guiclass": "HttpTestSampleGui", "testclass": "HTTPSamplerProxy",
"testname": sample.get('title'),
"enabled": "true"})
if sample.get("data").__len__() < 1 and not sample.get("queryData"):
elementProp = etree.SubElement(HTTPSamplerProxy, "elementProp")
cls.common_api(elementProp, {"name": "HTTPsampler.Arguments", "elementType": "Arguments",
"guiclass": "HTTPArgumentsPanel", "testclass": "Arguments",
"testname": "User Defined Variables", "enabled": "true"})
collectionProp = etree.SubElement(elementProp, "collectionProp")
collectionProp.set("name", "Arguments.arguments")
else:
boolProp = etree.SubElement(HTTPSamplerProxy, "boolProp")
boolProp.set("name", "HTTPSampler.postBodyRaw")
boolProp.text = "true"
elementProp = etree.SubElement(HTTPSamplerProxy, "elementProp")
cls.common_api(elementProp, {"name": "HTTPsampler.Arguments", "elementType": "Arguments"})
collectionProp = etree.SubElement(elementProp, "collectionProp")
collectionProp.set("name", "Arguments.arguments")
elementProp = etree.SubElement(collectionProp, "elementProp")
elementProp.set("name", "")
elementProp.set("elementType", "HTTPArgument")
boolProp = etree.SubElement(elementProp, "boolProp")
boolProp.set("name", "HTTPArgument.always_encode")
boolProp.text = "false"
stringProp = etree.SubElement(elementProp, "stringProp")
stringProp.set("name", "Argument.value")
# stringProp.text = json.dumps(sample.get("params")).replace(""", """)
json_data = sample.get("data")
stringProp.text = json.dumps(json_data, ensure_ascii=False, indent=4)
stringProp = etree.SubElement(elementProp, "stringProp")
stringProp.set("name", "Argument.metadata")
stringProp.text = "="
# host
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "HTTPSampler.domain")
# stringProp.text = data.get("host")
stringProp.text = data.get("")
# port
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "HTTPSampler.port")
# stringProp.text = data.get("port")
stringProp.text = data.get("")
# protocol
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "HTTPSampler.protocol")
# stringProp.text = "http"
stringProp.text = ""
# encoding
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "HTTPSampler.contentEncoding")
stringProp.text = "UTF-8"
# path
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "HTTPSampler.path")
stringProp.text = '${baseUrl}' + sample.get("url")
# method
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "HTTPSampler.method")
stringProp.text = sample.get("method").upper()
# follow redirects
boolProp = etree.SubElement(HTTPSamplerProxy, "boolProp")
boolProp.set("name", "HTTPSampler.follow_redirects")
boolProp.text = "true"
# auto redirects
boolProp = etree.SubElement(HTTPSamplerProxy, "boolProp")
boolProp.set("name", "HTTPSampler.auto_redirects")
boolProp.text = "false"
# use keepalive
boolProp = etree.SubElement(HTTPSamplerProxy, "boolProp")
boolProp.set("name", "HTTPSampler.use_keepalive")
boolProp.text = "true"
# DO MULTIPART POST
boolProp = etree.SubElement(HTTPSamplerProxy, "boolProp")
boolProp.set("name", "HTTPSampler.DO_MULTIPART_POST")
boolProp.text = "false"
# embedded url re
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "HTTPSampler.embedded_url_re")
stringProp.text = ""
# connect timeout
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "HTTPSampler.connect_timeout")
stringProp.text = ""
# response timeout
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "HTTPSampler.response_timeout")
stringProp.text = ""
# comments
stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
stringProp.set("name", "TestPlan.comments")
stringProp.text = sample.get("description")
hash_tree = etree.SubElement(shashTree, "hashTree")
@classmethod
def common_api(cls, obj_xml, datas):
"""
循环set 父节点
:param obj_xml:
:param datas:
:return:
"""
for key, value in datas.items():
obj_xml.set(key, value)
@classmethod
def conversion(cls, result, file_name):
"""
转换主函数
:param result: 接口数据
:param file_name: 测试计划名
:return:
"""
jmeterTestPlan = etree.Element("jmeterTestPlan")
hashTree = cls.jmeter_test_plan(jmeterTestPlan)
testPlan = cls.test_plan(hashTree, file_name)
threadGroup = cls.thread_group(testPlan)
cls.controller(threadGroup, result)
etree.tostring(jmeterTestPlan, encoding='UTF-8', pretty_print=True).decode()
tree = etree.ElementTree(jmeterTestPlan)
return tree
if __name__ == '__main__':
pass
创建jmx文件调用该方法:
import uuid
from utils.FILE_PATH import DOWNLOAD_PATH
from common.create_jmx import CreateJmx
class ToJmx(object):
@classmethod
def to_jmx(cls, name, data):
jmeter_xml = CreateJmx.conversion(data, name)
file_name = f"{name}-{uuid.uuid4().hex}.jmx"
try:
jmeter_xml.write(f'{DOWNLOAD_PATH}/{file_name}', pretty_print=True, xml_declaration=True,
encoding='utf-8')
except Exception as e:
err = "rap2转换jmx失败:"+str(e)
raise Exception(err)
return file_name
if __name__ == '__main__':
result = ToJmx.to_jmx(407,{})
print(result)
创建jmx文件命名时,这里也要拼接uuid,防止文件名重复,后期我们是要做成api接口的,防止并发的情况~
后续扩展
- 将上述功能,做成api,开放给小伙伴使用
- 对接api,做成前端页面,让小伙伴体验更完美
相关文章
- KDD 2022 | 快手提出基于因果消偏的观看时长预估模型D2Q,解决短视频推荐视频时长bias难题
- 单GPU实现20Hz在线决策,最新基于序列生成模型的高效轨迹规划方法解读
- 基于腾讯云开发微信小程序(新闻发布及共享平台)下
- chatGPT实战之「基于你的数据库,为你智能生成SQL」
- [开源推荐] 基于yii2的restful接口自动生成在线文档
- [Bioinformatics | 论文解读] 基于生成对抗网络的单细胞半监督注释和降维框架
- [J. Med. Chem. | 论文简读] RELATION:基于结构的新药设计深度生成模型
- 【每周CV论文推荐】基于GAN的图像对比度与色调映射增强值得阅读的文章
- 国内研究人员提出了一种基于触觉和视听刺激融合的脑电情感诱发范式
- 基于webpack,不使用任何脚手架,创建纯粹的webpack项目
- 基于SSM框架的电商平台后台管理系统
- 【数据挖掘】数据挖掘建模 ( 预测建模 | 描述建模 | 预测模型 | 描述模型 | 判别模型 | 概率模型 | 基于回归的预测模型 )
- 重压之下,何不蹭风口,割韭菜?币安顶风发布基于AI绘图技术的NFT生成器
- 基于『成交数据』的股票联动研究
- 信息自动删除基于Redis Java实现信息自动过期删除(redisjava过期)
- 基于文件验证的vsftpd虚拟用户
- 基于Redis的分布式ID生成策略(redisid生成)
- 传智汇 redis 基于内存的强大NoSQL存储(传智 redis)
- 后盾网基于Redis极大提升开发效率(后盾网redis)
- 生成分布式唯一ID生成方案基于Redis集群的全局ID生成(redis 集群全局id)
- 实现基于Redis的分布式锁Java实现(redis锁java代码)
- 基于PHP生成静态页的实现方法