目录
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文件
<?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代码
3. 运行.jmx文件,即运行性能用例
3.1 编写MyJMeterUtil.java,作为工具类
在 MyJMeterUtil.java 类中,编写多个方法,每个方法对应不同的组件。
最后的run()方法,即调用各个方法,生成多个组件,再根据组件之间的相互关系,实施“嵌套”,保存测试计划成.jmx文件。
然后利用反射原理,获取JMeter的runNoGui()方法,运行方法,即运行.jmx文件,开展性能测试,保存测试结果,报告等。
MyJMeterUtil.java的完整源码,见文末。
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();
}
}
}