目录

1. 为什么需要做JMeter二次开发?

2. 如何通过java代码的形式,生成.jmx文件?

2.1 利用JMeter,生成.jmx文件

2.2 通过java编码的形式,生成.jmx文件

3. 运行.jmx文件,即运行性能用例

3.1 编写MyJMeterUtil.java,作为工具类

3.2 在Service层中,调用MyJMeterUtil.java工具类

附录:MyJMeterUtil.java 类的完整源码


1. 为什么需要做JMeter二次开发?

JMeter作为一款开源的性能、接口测试工具,有时候无法满足我们工作的需要,一般体现在:协议不支持、没有相应数据处理功能等。

2. 如何通过java代码的形式,生成.jmx文件?

2.1 利用JMeter,生成.jmx文件

按照常规的做法,是启动JMeter,根据业务需求,创建测试计划(线程组,配置元件,取样器,正则提取器,控制器,响应断言等),保存后生成.jmx文件。

2.2 通过java编码的形式,生成.jmx文件

可以先查看JMeter工具的源码,模仿JMeter的源码,编写自己的MyJMeterUtil.java.

(1)通过JMeter工具界面,生成一个.jmx文件

java 生成mpp文件 java生成jmx文件_java 生成mpp文件

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.1.1 r1855137">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="测试计划" enabled="true">
      <stringProp name="TestPlan.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>
      <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV 数据文件设置" enabled="true">
        <stringProp name="delimiter">,</stringProp>
        <stringProp name="fileEncoding">UTF-8</stringProp>
        <stringProp name="filename">C:/Users/pearl/Desktop/road/2022/paramFile/test0113.csv</stringProp>
        <boolProp name="ignoreFirstLine">false</boolProp>
        <boolProp name="quotedData">false</boolProp>
        <boolProp name="recycle">true</boolProp>
        <stringProp name="shareMode">shareMode.all</stringProp>
        <boolProp name="stopThread">false</boolProp>
        <stringProp name="variableNames">id,name</stringProp>
      </CSVDataSet>
      <hashTree/>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="线程组" enabled="true">
        <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>
          <stringProp name="LoopController.loops">3</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">2</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP请求" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
            <collectionProp name="Arguments.arguments">
              <elementProp name="id" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value"></stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">id</stringProp>
              </elementProp>
              <elementProp name="name" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value"></stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">name</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="HTTPSampler.domain">www.baidu.com</stringProp>
          <stringProp name="HTTPSampler.port">80</stringProp>
          <stringProp name="HTTPSampler.protocol">http</stringProp>
          <stringProp name="HTTPSampler.contentEncoding">utf-8</stringProp>
          <stringProp name="HTTPSampler.path"></stringProp>
          <stringProp name="HTTPSampler.method">POST</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息头管理器" enabled="true">
            <collectionProp name="HeaderManager.headers">
              <elementProp name="" elementType="Header">
                <stringProp name="Header.name"></stringProp>
                <stringProp name="Header.value"></stringProp>
              </elementProp>
              <elementProp name="" elementType="Header">
                <stringProp name="Header.name"></stringProp>
                <stringProp name="Header.value"></stringProp>
              </elementProp>
            </collectionProp>
          </HeaderManager>
          <hashTree/>
          <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="正则表达式提取器" enabled="true">
            <stringProp name="RegexExtractor.useHeaders">false</stringProp>
            <stringProp name="RegexExtractor.refname"></stringProp>
            <stringProp name="RegexExtractor.regex"></stringProp>
            <stringProp name="RegexExtractor.template"></stringProp>
            <stringProp name="RegexExtractor.default"></stringProp>
            <stringProp name="RegexExtractor.match_number"></stringProp>
          </RegexExtractor>
          <hashTree/>
          <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="响应断言" enabled="true">
            <collectionProp name="Asserion.test_strings">
              <stringProp name="49586">200</stringProp>
            </collectionProp>
            <stringProp name="Assertion.custom_message"></stringProp>
            <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
            <boolProp name="Assertion.assume_success">false</boolProp>
            <intProp name="Assertion.test_type">16</intProp>
          </ResponseAssertion>
          <hashTree/>
        </hashTree>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

(2)参考上一步中的.jmx文件中各个组件之间的相互包含关系,每个组件的属性等信息,编写.java代码

java 生成mpp文件 java生成jmx文件_jmeter_02

java 生成mpp文件 java生成jmx文件_HTTPS_03

3. 运行.jmx文件,即运行性能用例

3.1 编写MyJMeterUtil.java,作为工具类

在 MyJMeterUtil.java 类中,编写多个方法,每个方法对应不同的组件。

最后的run()方法,即调用各个方法,生成多个组件,再根据组件之间的相互关系,实施“嵌套”,保存测试计划成.jmx文件。

然后利用反射原理,获取JMeter的runNoGui()方法,运行方法,即运行.jmx文件,开展性能测试,保存测试结果,报告等。

MyJMeterUtil.java的完整源码,见文末。

java 生成mpp文件 java生成jmx文件_测试计划_04

3.2 在Service层中,调用MyJMeterUtil.java工具类

....................
//执行性能用例
    public void execPerfCase(Integer perfCaseId){
        List<CaseDto> testCases = getCasesByPerfCaseId(perfCaseId);
        PerfCaseDto perfCaseDto = findPerfCaseById(perfCaseId);

        for(CaseDto caseDto: testCases){
            Integer caseId = caseDto.getId();
            RegExtractor regExtractor = regExtractorRepo.findRegExtractorByCaseId(caseId);
            RespAssertion respAssertion = respAssertionRepo.findRespAssertionByCaseId(caseId);

            if(regExtractor != null){
                caseDto.setRegExtractor(regExtractor);
            }
            if(respAssertion != null){
                caseDto.setRespAssertion(respAssertion);
            }
        }

        UUID uuid = UUID.randomUUID();
        PerfResult perfResult = new PerfResult();
        perfResult.setPerfCaseId(perfCaseDto.getId());
        perfResult.setStatus(0);
        perfResult.setResultUrl("/jmeterReport/" + uuid + "/report/index.html"); //动静分离?
        perfResultRepo.save(perfResult);

        try {
            perfResult.setStatus(1);
            perfResult.setExecTime(0L);
            perfResultRepo.save(perfResult);

            long start = System.currentTimeMillis();
            MyJMeterUtil.run(perfCaseDto, testCases, uuid.toString()); //调用MyJMeterUtil.java工具类
            long end = System.currentTimeMillis();
            perfResult.setExecTime((end - start)/1000);

            perfResult.setStatus(2);
            perfResultRepo.save(perfResult);

        } catch (Exception e){
            e.printStackTrace();
            perfResult.setStatus(3);
            perfResultRepo.save(perfResult);
        }

    }
....................

附录:MyJMeterUtil.java 类的完整源码

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.road.testdev.autotest.bean.dto.CaseDto;
import com.road.testdev.autotest.bean.dto.perf.PerfCaseDto;
import com.road.testdev.autotest.bean.http.HttpHeader;
import com.road.testdev.autotest.entity.perf.RegExtractor;
import com.road.testdev.autotest.entity.perf.RespAssertion;
import lombok.extern.slf4j.Slf4j;
import org.apache.jmeter.JMeter;
import org.apache.jmeter.assertions.ResponseAssertion;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.CSVDataSet;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.extractor.RegexExtractor;
import org.apache.jmeter.protocol.http.control.Header;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.threads.ThreadGroup;
import org.apache.jorphan.collections.ListedHashTree;

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Slf4j
public class MyJMeterUtil {
    //public static final String JMETER_HOME = "C:/apache-jmeter-5.1.1";
    public static final String JMETER_HOME = "/Users/sheryltang/apache-jmeter-5.4.1";
    public static final String JMETER_PROP = JMETER_HOME + "/bin/jmeter.properties";
    public static final String SAVE_SERVICE_PROP = JMETER_HOME + "/bin/saveservice.properties";
    public static final String USER_PROP = JMETER_HOME + "/bin/user.properties";
    public static final String UPGRADE_PROP = "/bin/upgrade.properties"; //特殊
    public static final String SYSTEM_PROP = JMETER_HOME + "/bin/system.properties";

    public static final String TEST_FILE = "C:/Users/pearl/Desktop/road/Lessons/sourcecode/jmxFiles/";
    //public static final String OUTPUT_DIR = "src/main/resources/static/output/";
    public static final String OUTPUT_DIR = "C:/files/jmeterReport/";


    static {
        JMeterUtils.setJMeterHome(JMETER_HOME);
        JMeterUtils.loadJMeterProperties(JMETER_PROP);
        JMeterUtils.setProperty("saveservice_properties", SAVE_SERVICE_PROP);
        JMeterUtils.setProperty("user_properties", USER_PROP);
        JMeterUtils.setProperty("upgrade_properties", UPGRADE_PROP);
        JMeterUtils.setProperty("system_properties", SYSTEM_PROP);
        //JMeterUtils.setProperty("jmeter.reportgenerator.outputdir", OUTPUT_DIR + "/report");
    }

    public static TestPlan getTestPlan(PerfCaseDto perfCaseDto){
        TestPlan testPlan = new TestPlan(perfCaseDto.getPerfCaseName()); //注意
        testPlan.setEnabled(true);
        testPlan.setName("My Test Plan");
        testPlan.setProperty(TestElement.GUI_CLASS, "TestPlanGui");
        testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName());

        testPlan.setComment("Test Plan Comment"); //注释
        testPlan.setFunctionalMode(false); //函数测试模式
        testPlan.setTearDownOnShutdown(true); //主线程结束后运行teardown线程组
        testPlan.setSerialized(false); //独立运行每个线程组
        testPlan.setTestPlanClasspath(""); //添加目录或jar包到ClassPath

        //针对elementProp的处理
        Arguments arguments = new Arguments();
        arguments.setEnabled(true);
        arguments.setName("用户定义的变量"); //用户定义的变量, Arguments Name
        arguments.setProperty(TestElement.GUI_CLASS, "ArgumentsPanel");
        arguments.setProperty(TestElement.TEST_CLASS, "Arguments");
        //如果有具体的用户定义的变量,这里还要嵌套collectionProp, 然后elementProp...

        testPlan.setUserDefinedVariables(arguments);  //注意这里如何将elementProp放进testPlan里

        return testPlan;
    }

    public static ThreadGroup getThreadGroup(PerfCaseDto perfCaseDto){
        Integer threadNum = perfCaseDto.getThreadNum();
        double rampUpTime = perfCaseDto.getRampUpTime();
        Integer runType = perfCaseDto.getRunType();
        Integer runCount = perfCaseDto.getRunCount();

        ThreadGroup threadGroup = new ThreadGroup();
        threadGroup.setEnabled(true);
        threadGroup.setName("Thread Group 1");
        threadGroup.setProperty(TestElement.GUI_CLASS, "ThreadGroupGui");
        threadGroup.setProperty(TestElement.TEST_CLASS, "ThreadGroup");

        threadGroup.setProperty("ThreadGroup.on_sample_error", "continue");
        threadGroup.setNumThreads(threadNum);
        threadGroup.setRampUp((int) rampUpTime);
        threadGroup.setScheduler(runType == 1); //重要!!! "运行方式, 1:按时间,2:按次数"
        threadGroup.setDuration(runCount); //重要
        threadGroup.setDelay(0);
        threadGroup.setIsSameUserOnNextIteration(true); //例子jmx中无这项,别遗漏

        LoopController loopController = new LoopController();
        loopController.setEnabled(true);
        loopController.setName("loop controller"); //循环控制器
        loopController.setProperty(TestElement.GUI_CLASS, "LoopControlPanel");
        loopController.setProperty(TestElement.TEST_CLASS, "LoopController");
        loopController.setContinueForever(false);
        loopController.setLoops(runType == 1 ? -1 : runCount); //loops == -1表示永远循环, "运行方式, 1:按时间,2:按次数"

        threadGroup.setSamplerController(loopController);

        return threadGroup;
    }

    public static HTTPSamplerProxy getHttpSamplerProxy(CaseDto caseDto) throws Exception {
        String caseName = caseDto.getCaseName();
        String method = caseDto.getMethod();
        String url = caseDto.getUrl();
        String body = caseDto.getBody();

        URL uri = new URL(url);
        String protocol = uri.getProtocol();
        String host = uri.getHost();
        String path = uri.getPath();
        String query = uri.getQuery();
        int port = uri.getPort();  //需要额外处理,测试的时候取出来,有-1的情况

        if(port == -1){
            if("http".equals(protocol)){
                port = 80;
            }
            if("https".equals(protocol)){
                port = 443;
            }
        }

        HTTPSamplerProxy httpSamplerProxy = new HTTPSamplerProxy();
        httpSamplerProxy.setEnabled(true);
        httpSamplerProxy.setName(caseName);
        httpSamplerProxy.setProperty(TestElement.GUI_CLASS, "HttpTestSampleGui");
        httpSamplerProxy.setProperty(TestElement.TEST_CLASS, "HTTPSamplerProxy");

        httpSamplerProxy.setDomain(host); //域名或IP
        httpSamplerProxy.setPort(port); //端口
        httpSamplerProxy.setProtocol(protocol); //协议
        httpSamplerProxy.setContentEncoding("utf-8"); //内容编码
        if(Objects.nonNull(query) && !"".equals(query)){ //老师写Objects.nonNull(query) && "".equals(query)
            httpSamplerProxy.setPath(path + "?" + query); //路径
        }else{
            httpSamplerProxy.setPath(path); //路径
        }


        httpSamplerProxy.setFollowRedirects(true); //跟随重定向
        httpSamplerProxy.setAutoRedirects(false); //自动重新定向
        httpSamplerProxy.setUseKeepAlive(true); //使用keepalive
        httpSamplerProxy.setDoMultipart(false); //对POST使用multipart/form-data
        httpSamplerProxy.setEmbeddedUrlRE(""); //与浏览器兼容的头
        httpSamplerProxy.setConnectTimeout("");
        httpSamplerProxy.setResponseTimeout("");

        Arguments arguments = new Arguments();
        arguments.setEnabled(true);
        arguments.setName("用户定义的变量");
        arguments.setProperty(TestElement.GUI_CLASS, "HTTPArgumentsPanel");
        arguments.setProperty(TestElement.TEST_CLASS, "Arguments");

        httpSamplerProxy.setMethod("GET"); //方法,需要分情况处理,所以放在后面, 默认先GET

        if("post".equalsIgnoreCase(method)){
            List<HTTPArgument> collectionProp = new ArrayList<>();   //可能有多个用户变量
            HTTPArgument httpArgument = new HTTPArgument();

            httpSamplerProxy.setPostBodyRaw(true); //跟方法是get还是post有关
            httpSamplerProxy.setMethod("POST"); //原来默认是GET,这里改成POST

            httpArgument.setAlwaysEncoded(false);
            httpArgument.setValue(body);
            httpArgument.setMetaData("=");

            collectionProp.add(httpArgument);

            arguments.setProperty(new CollectionProperty("Arguments.arguments", collectionProp));
        }

        httpSamplerProxy.setArguments(arguments);

        return httpSamplerProxy;
    }

    public static CSVDataSet getCsvDataSet(PerfCaseDto perfCaseDto){
        String csvFilePath = perfCaseDto.getCsvFilePath();
        String csvVarName = perfCaseDto.getCsvVarName();

        if(csvFilePath == null && "".equals(csvFilePath)){
            log.info("csvFilePath is null");
            return null;
        }

        CSVDataSet csvDataSet = new CSVDataSet();
        csvDataSet.setEnabled(true);
        csvDataSet.setName("My CSV Data Set");
        csvDataSet.setProperty(TestElement.GUI_CLASS, "TestBeanGUI");
        csvDataSet.setProperty(TestElement.TEST_CLASS, "CSVDataSet");

        //注意,这里需要通过setProperty来设置这些属性,否则jmeter文件里读不到这里设置的值
        csvDataSet.setProperty("delimiter", ","); //分隔符
        csvDataSet.setProperty("fileEncoding", "utf-8"); //文件编码
        csvDataSet.setProperty("filename", csvFilePath); // 文件名
        csvDataSet.setProperty("ignoreFirstLine", true); //忽略首行
        csvDataSet.setProperty("quotedData", false); //是否允许带引号
        csvDataSet.setProperty("recycle", true); // 遇到问你件结束符是否再次循环
        csvDataSet.setProperty("shareMode", "shareMode.all");  // 线程共享模式
        csvDataSet.setProperty("stopThread", false);  //遇到文件结束是否停止线程
        csvDataSet.setProperty("variableNames", csvVarName); //变量名称,西文逗号间隔

        return csvDataSet;
    }

    public static HeaderManager getHeaderManager(CaseDto caseDto){
        List<HttpHeader> headers = caseDto.getHttpHeaders();

        HeaderManager headerManager = new HeaderManager();
        headerManager.setEnabled(true);
        headerManager.setName("My Http Header Manager");
        headerManager.setProperty(TestElement.GUI_CLASS, "HeaderPanel");
        headerManager.setProperty(TestElement.TEST_CLASS, "HeaderManager");

        List<Header> headerList = new ArrayList<>();
        for(HttpHeader header: headers){
            Header tmp = new Header(header.getKey(), header.getValue());
            headerList.add(tmp);
        }

        headerManager.setProperty(new CollectionProperty("HeaderManager.headers", headerList));

        return headerManager;
    }

    public static RegexExtractor getRegexExtractor(CaseDto caseDto){
        RegExtractor regExtractor = caseDto.getRegExtractor();

        if(regExtractor ==  null){
            return null;
        }

        RegexExtractor regexExtractor = new RegexExtractor();
        regexExtractor.setEnabled(true);
        regexExtractor.setName("My Regex Extractor"); //正则表达式提取器
        regexExtractor.setProperty(TestElement.GUI_CLASS, "RegexExtractorGui");
        regexExtractor.setProperty(TestElement.TEST_CLASS, "RegexExtractor");

        regexExtractor.setUseField("false");
        //regexExtractor.setRefName(regExtractor.getRegExpression()); //正则表达式做名称?
        regexExtractor.setRefName(regExtractor.getVarName()); //引用名称
        regexExtractor.setRegex(regExtractor.getRegExpression()); //正则表达式
        regexExtractor.setTemplate(regExtractor.getTemplate()); //模版
        regexExtractor.setDefaultValue(regExtractor.getDefaultValue()); //缺省值,默认值
        regexExtractor.setMatchNumber(regExtractor.getMatchNum()); //匹配数字

        return regexExtractor;
    }

    public static ResponseAssertion getResponseAssertion(CaseDto caseDto){
        RespAssertion respAssertion = caseDto.getRespAssertion();

        if(respAssertion == null){
            return null;
        }

        ResponseAssertion responseAssertion = new ResponseAssertion();
        responseAssertion.setEnabled(true);
        responseAssertion.setName("My Response Assertion"); //响应断言
        responseAssertion.setProperty(TestElement.GUI_CLASS, "AssertionGui");
        responseAssertion.setProperty(TestElement.TEST_CLASS, "ResponseAssertion");

        responseAssertion.setCustomFailureMessage("");
        responseAssertion.setTestFieldResponseData();
        responseAssertion.setAssumeSuccess(false);
        responseAssertion.setToSubstringType();

        String patterns = respAssertion.getPatterns(); //CaseDto中的patterns是String
        Gson gson = new Gson();
        Type type = new TypeToken<List<String>>(){}.getType();
        List<String> list = gson.fromJson(patterns, type);

        responseAssertion.setProperty(new CollectionProperty("Asserion.test_strings", list));

        return responseAssertion;
    }

    //这里参数要加一个uuid,从service那便传过来,用于构建report路径
    public static void run(PerfCaseDto perfCaseDto, List<CaseDto> testCases, String uuid) throws Exception {

        JMeterUtils.setProperty("jmeter.reportgenerator.outputdir", OUTPUT_DIR + uuid + "/report");

        try {
            String jmxFileName = TEST_FILE + uuid + ".jmx"; //jmx的路径和名称
            TestPlan testPlan = getTestPlan(perfCaseDto);
            ThreadGroup threadGroup = getThreadGroup(perfCaseDto);
            CSVDataSet csvDataSet = getCsvDataSet(perfCaseDto);

            List<ListedHashTree> httpSamplerProxyTrees = new ArrayList<>();
            testCases.forEach(caseDto -> {
                try {
                    ListedHashTree httpSamplerProxyTree = new ListedHashTree();
                    HTTPSamplerProxy httpSamplerProxy = getHttpSamplerProxy(caseDto);
                    HeaderManager headerManager = getHeaderManager(caseDto);
                    RegexExtractor regexExtractor = getRegexExtractor(caseDto);
                    ResponseAssertion responseAssertion = getResponseAssertion(caseDto);

                    httpSamplerProxyTree.add(httpSamplerProxy, headerManager);  //加请求头
                    httpSamplerProxyTree.add(httpSamplerProxy, regexExtractor);  //加上正则表达式提取器
                    httpSamplerProxyTree.add(httpSamplerProxy, responseAssertion); //加上响应断言

                    httpSamplerProxyTrees.add(httpSamplerProxyTree);    //往取样器树中逐个加取样器

                } catch (Exception e) {
                    e.printStackTrace();
                }

            });

            ListedHashTree threadGroupTree = new ListedHashTree();
            httpSamplerProxyTrees.forEach(httpSamplerProxyTree -> { //遍历的方式往线程组中加取样器树
                threadGroupTree.add(threadGroup, httpSamplerProxyTree);
            });

            ListedHashTree testPlanTree = new ListedHashTree();
            testPlanTree.add(testPlan, threadGroupTree);    //测试计划中加线程组
            if(csvDataSet != null){
                testPlanTree.add(csvDataSet);   //先加线程组,后加csv,否则报错
            }

            OutputStream os = new FileOutputStream(jmxFileName);    //新建输出流,jmx文件
            SaveService.saveTree(testPlanTree, os); //保存测试计划树

            JMeter jmeter = new JMeter();
            Class<? extends JMeter> jmeterClass = jmeter.getClass();
            Method method = jmeterClass.getDeclaredMethod("runNonGui", String.class, String.class, boolean.class, String.class, boolean.class);
            method.setAccessible(true);
            method.invoke(jmeter, jmxFileName, OUTPUT_DIR + uuid + "/logFile", false, null, true);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}