swagger-ui 接口文档一键生成 jmx 文件供 jmeter 使用

背景:

由于接口文档编写耗时,而且需要持续维护,耗时耗力,后来发现一款 swagger 工具非常好用,可以一键自动生成接口文档,目前可视化已经在项目线上使用。

对于测试而言,自动化接口脚本也一样存在录制,编写,维护更新耗时耗力,于是想着能不能有种方法可以将 swagger-ui 文档转换为 jmx 文件?

提取 swagger 中需要的数据,代码如下

import requests
import json

def get_test_plan():
    url = ""
    response = requests.get(url)
    data = json.loads(response.text)
    print(data.get("swagger"))
    host = data.get("host")
    base_path = data.get("basePath")
    path = data.get("paths")
    thread_groups = data.get("tags")
    definitions = data.get("definitions")
    for thread_group in thread_groups:
        thread_group['host'] = str(host).split(":")[0]
        thread_group["port"] = str(host).split(":")[1]
        thread_group['sample'] = []
        for path_key,path_value in path.items():
            if isinstance(path_value,dict):
                for method,sample_value in path_value.items():
                    if isinstance(sample_value,dict):
                        if sample_value.get("tags")[0] == thread_group.get("name"):
                            parameters = {}
                            if isinstance(sample_value.get("parameters"),list):
                                if sample_value.get("parameters").__len__()>1:
                                    for param in sample_value.get("parameters"):
                                        parameters[param.get("name")] = "${" + param.get("name") + "}"
                                else:
                                    for param in sample_value.get("parameters"):
                                        model_name = (param.get("name"))[0].upper()+(param.get("name"))[1:]
                                        if model_name in list(definitions.keys()):
                                            model_value = definitions.get(model_name)
                                            for param_name,param_value in model_value.get("properties").items():
                                                parameters[param_name] = "${" + param_name + "}"
                            thread_group['sample'].append({"path":base_path+path_key,"method":method,"params":parameters,"sampler_comments":sample_value.get("description")})

    return thread_groups


if __name__ == '__main__':
    thread_group = get_test_plan()
    print(thread_group)

生成后数据如下:

生成 jmx 文件:

# *_*coding:utf-8 *_*
import json
from lxml import etree
import ThreadGroup
def jmeter_test_plan(root_xml):
    JmeterTestPlan = root_xml
    JmeterTestPlan.set('version','1.2')
    JmeterTestPlan.set('properties','2.3')
    JmeterTestPlan.set('jmeter','5.1.1 r1855137')
    return etree.SubElement(JmeterTestPlan,'hashTree')

def test_plan(parent_xml):
    testPlan = etree.SubElement(parent_xml,'TestPlan')
    testPlan.set("guiclass","TestPlanGui")
    testPlan.set("testclass","TestPlan")
    testPlan.set("testname","Test Plan")
    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 = "true"
    boolProp3 = etree.SubElement(testPlan,"boolProp")
    boolProp3.set("name","TestPlan.serialize_threadgroups")
    boolProp3.text = "false"
    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')

def thread_group(parent_xml):
    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")
    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"
    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")

def controller(parent_xml,result):
    for data in result:
        GenericController = etree.SubElement(parent_xml, "GenericController")
        GenericController.set("guiclass", "LogicControllerGui")
        GenericController.set("testclass", "GenericController")
        GenericController.set("testname", data.get("name"))
        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("sample"):
            HTTPSamplerProxy = etree.SubElement(shashTree, "HTTPSamplerProxy")
            common_api(HTTPSamplerProxy,
                       {"guiclass": "HttpTestSampleGui", "testclass": "HTTPSamplerProxy",
                        "testname": sample.get('path'),
                        "enabled": "true"})
            if sample.get("params").__len__() < 1:
                elementProp = etree.SubElement(HTTPSamplerProxy, "elementProp")
                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")
                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("\"", "&quot;")
                stringProp.text = json.dumps(sample.get("params"))
                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")

            ###port
            stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
            stringProp.set("name", "HTTPSampler.port")
            stringProp.text = data.get("port")

            ##protocol
            stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
            stringProp.set("name", "HTTPSampler.protocol")
            stringProp.text = "http"

            # encoding
            stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
            stringProp.set("name", "HTTPSampler.contentEncoding")
            stringProp.text = "utf8"

            # path
            stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
            stringProp.set("name", "HTTPSampler.path")
            stringProp.text = sample.get("path")

            # method
            stringProp = etree.SubElement(HTTPSamplerProxy, "stringProp")
            stringProp.set("name", "HTTPSampler.method")
            stringProp.text = sample.get("method")

            # 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("sampler_comments")
            hash_tree = etree.SubElement(shashTree, "hashTree")




def common_api(obj_xml,datas):
    for key,value in datas.items():
        obj_xml.set(key,value)


if __name__ == '__main__':
    result = ThreadGroup.get_test_plan()
    jmeterTestPlan = etree.Element("jmeterTestPlan")
    hashTree = jmeter_test_plan(jmeterTestPlan)
    testPlan = test_plan(hashTree)
    threadGroup = thread_group(testPlan)
    control = controller(threadGroup,result)
    print(etree.tostring(jmeterTestPlan,pretty_print=True))
    tree = etree.ElementTree(jmeterTestPlan)
    tree.write('text.xml', pretty_print=True, xml_declaration=True, encoding='utf-8')

生成文件如下:text.xml

用 jmeter 打开生成的文件:


↙↙↙阅读原文可查看相关链接,并与作者交流