Jmeter源码改造之-自动化生成性能测试脚本

Jmeter因其开源、扩展性好等原因,已经被业界广泛接受,有越来越多的企业选择Jmeter作为主要的压测工具。如果你想大幅降低Jmeter工具是使用的技术门槛,或者想大幅提升Jmeter脚本编写的效率,那么可以尝试开发功能来自动化生成相应脚本。

通常来说,有两种方式来自动化生成Jmeter脚本:

1.解析并生成jmx文件(xml文件)。在Jmeter工具中编写和配置脚本完成以后,都可以保存为jmx文件,这个文件格式是标准的xml文件,其基本是hashTree树的结构。通过研究现有jmx文件,可以找到其构造规律和方法。之后,根据性能测试需求,就可以构造出想要的jmx脚本了。

2.源码解析和改造。通过改造Jmeter界面和功能,结合业务需求(比如是Excel文件的需求)在Jmeter窗口中自动生成测试计划树。

下面我们就来详细讲解下第2种方法:

【一、实现功能概述】

将性能测试需求结构化,比如Excel;定制Jmeter菜单,增加一个导入的菜单功能;该功能可选中Excel,解析Excel,并将需求自动转换为Jmeter测试计划、线程组、HTTP请求、JSON消息体数据、CSV配置等内容。

【二、性能测试需求结构化】

需求方面,可以根据各个企业情况自己定义,建议是整理到Excel中,便于后续代码解析使用。下图是样例:

【三、环境准备】+【定制Jmeter菜单】

可以参考我写的另一篇文章:

Jmeter源码改造之-定制Jmeter菜单-CSDN博客

【四、新建目录-存放数据文件】

该目录主要存放生成的jmx文件、CSV文件

核心代码如下:

//新建目录用于存放数据文件
String dataDir = f.getParent()+"\\数据文件";
if(!DirectoryOperations.isDirectoryExists(dataDir)) {
    DirectoryOperations.createNewDirectory(dataDir);
}
else{
    File directory = new File(dataDir);
    deleteDirectory(directory);
    DirectoryOperations.createNewDirectory(dataDir);
}

注意:

1.f为File类型,就是第二步中的Excel文件。

2. DirectoryOperations是自定义的类,可以自己定义一些文件目录操作的函数,供后续调用。

【五、解析Excel】

核心代码:

Map<String,String> mapData = readExcel(f);
//自定义readExcel函数,用于解析Excel。以下示例代码:
Workbook workbook = new XSSFWorkbook(f);
Sheet sheet = workbook.getSheetAt(0);

Row row = sheet.getRow(1);
Cell cell = row.getCell(0);
if (Objects.equals(cell.getStringCellValue(), "测试项目:")) {
    if (row.getCell(1) == null) {
        System.out.println("测试项目不能为空!");
    } else {
        mapData.put("测试项目:", row.getCell(1).getStringCellValue());
    }
}

注意:

1.解析完Excel后,将键值对存储到mapData中。

2. readExcel需要自定义。

3.建议是用poi的包解析Excel,我使用的版本是4.0.0

【六、构造测试计划树】

核心代码:

HashTree fhTree = createFHTree(mapData, dataDir);

注意:

1.自定义createFHTree函数,构造测试计划树。

2. createFHTree函数需要第四、第五步结果作为参数。

【六-1、构建测试计划】

核心代码:

//测试计划基本参数设置
TestPlan testPlan = new TestPlan(getMapValue(mapData,"测试项目:"));
testPlan.setFunctionalMode(false);
testPlan.setTearDownOnShutdown(true);
testPlan.setComment(getMapValue(mapData,"测试项目注释:"));
testPlan.setSerialized(false);

//测试计划中用户自定义变量的设置,同时更新到Json请求中
Arguments userDefinedArgs = new Arguments();
String jsonString = getMapValue(mapData, "JSON请求:");

//需要自己根据Excel需求中的内容,自己替换JsonString中的内容,替换部分的示例如下:
"ruleidid": "${__RandomFromMultipleVars(ruleidid1|ruleidid2|ruleidid3|ruleidid4|ruleidid5|ruleidid6)}",

//需要自己根据Excel需求中的内容,添加参数变量,示例代码如下:
userDefinedArgs.addArgument("ruleidid1", "1");
userDefinedArgs.addArgument("ruleidid1", "2");

//增加测试计划的参数变量
testPlan.setUserDefinedVariables(userDefinedArgs);

//测试计划界面刷新
testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName());
testPlan.setProperty(TestElement.GUI_CLASS, TestPlanGui.class.getName());

【六-2、构建线程组】

核心代码:

SetupThreadGroup threadGroup = new SetupThreadGroup();
threadGroup.setName(getMapValue(mapData, "测试项目:") + "-线程组");
threadGroup.isEnabled();
//获取Excel中的并发数,并设置到线程组
String numThreads = getMapValue(mapData, "性能要求-并发用户数:");
if (!Objects.equals(numThreads, "")) {
    if (numThreads.contains(".")) {
        int dotIndex = numThreads.indexOf(".");
        String newNumThreads = numThreads.substring(0, dotIndex);
        threadGroup.setNumThreads(Integer.parseInt(newNumThreads));
    } else {
        threadGroup.setNumThreads(Integer.parseInt(numThreads));
    }
}
//其他属性都可以通过SetupThreadGroup去设置,示例如下:
threadGroup.setRampUp(0);
threadGroup.setScheduler(false);

threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName());
threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName());

【六-3、构建CSV配置器】

核心代码:

// 写入CSV文件,同时更新CSV配置器,同时更新Json请求
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String timeStamp = dateFormat.format(new Date());
// paramName可以自定义String,也可以从Excel文件中获取
String filePath = dataDir + "\\CSVData" + "-" + paramName + "-" + timeStamp + ".csv";
// 写入csv文件,csv需要根据Excel文件,自己构造String
try (FileWriter writer = new FileWriter(filePath);
     BufferedWriter bufferedWriter = new BufferedWriter(writer)) {
    bufferedWriter.write(csvString);
} catch (IOException e) {
    e.printStackTrace();
}
//类似于六-1,需要自己根据Excel需求中的内容,自己构建和更新JsonString中的内容,替换部分的示例如下
"username":"${username}",
        "password":"${password}"

//设置CSV属性
CSVDataSet csvData = new CSVDataSet();
csvData.setName(getMapValue(mapData, "测试项目:") + "-CSV文件设置");
csvData.setProperty("filename", filePath);
csvData.setProperty("fileEncoding", "UTF-8");
//注意paramName、paramValue需自己构造,并且于上一步的变量比如username、password保持一致。
csvData.setProperty("variableNames", paramName + "," + paramValue);
//其他CSV配置器参数,都可以通过CSVDataSet类设置,示例如下:
csvData.setDelimiter(",");
csvData.setShareMode("All threads");
csvData.setProperty(TestElement.TEST_CLASS, CSVDataSet.class.getName());
csvData.setProperty(TestElement.GUI_CLASS, TestBeanGUI.class.getName());
//csv配置器添加到测试计划中
HashTree csvTree = new HashTree();
csvTree.add(csvData);
testPlan.addTestElement(csvData);
tree.add(testPlan, csvTree);

【六-4,构建HTTP头】

HeaderManager headerManager = new HeaderManager();
headerManager.setName(getMapValue(mapData, "测试项目:") + "-请求头管理器");
headerManager.isEnabled();
headerManager.setComment(getMapValue(mapData,"测试项目:")+"-请求头管理器");
headerManager.add(new Header("Content-Type", "application/json"));
headerManager.setProperty(TestElement.TEST_CLASS, HeaderManager.class.getName());
headerManager.setProperty(TestElement.GUI_CLASS, HeaderPanel.class.getName());

【六-5,构建HTTP请求】

核心代码:

HTTPSampler httpSampler = new HTTPSampler();
//注意下面参数值,可根据Excel内容,去自己获取
httpSampler.setProtocol(protocol);
httpSampler.setDomain(domain);
httpSampler.setPort(443);
httpSampler.setPath(path);
httpSampler.setMethod(httpMethod);
httpSampler.setName(httpName);
httpSampler.setContentEncoding("UTF-8");
//设置http请求的 Json参数
Arguments args = new Arguments();
//jsonString,就是六-1、六-3构建的新的json请求
args.addArgument("", jsonString);
convertArgumentsToHTTP(args);
httpSampler.setArguments(args);
httpSampler.setPostBodyRaw(true);
//http请求界面刷新
httpSampler.setProperty(TestElement.TEST_CLASS, HTTPSampler.class.getName());
httpSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName());

【六-6,构建查看树结果】

核心代码:

ResultCollector resultTree = new ResultCollector(new Summariser());
resultTree.setName(getMapValue(mapData, "测试项目:") + "-察看结果树");
resultTree.setProperty(TestElement.TEST_CLASS, ResultCollector.class.getName());
resultTree.setProperty(TestElement.GUI_CLASS, ViewResultsFullVisualizer.class.getName());

【六-7,构建汇总报告】

核心代码:

ResultCollector resultCollector = new ResultCollector(new Summariser());
resultCollector.setName(getMapValue(mapData, "测试项目:") + "-汇总报告");
resultCollector.setProperty(TestElement.TEST_CLASS, ResultCollector.class.getName());
resultCollector.setProperty(TestElement.GUI_CLASS, SummaryReport.class.getName());

【六-8,Jmeter树的构建】

HashTree subTree = new HashTree();
subTree.add(threadGroup);
subTree.add(threadGroup, httpSampler);
subTree.add(threadGroup, headerManager);
subTree.add(threadGroup, resultTree);
subTree.add(threadGroup, resultCollector);

testPlan.addThreadGroup(threadGroup);
tree.add(testPlan, subTree);

return tree;

【六-9、保存jmx数据文件】

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String timeStamp = dateFormat.format(new Date());
String jmxFileName = dataDir + "_" + timeStamp + ".jmx";
SaveService.saveTree(fhTree, new FileOutputStream(jmxFileName));

【六-10、将以上构造的整体数据加载Jmeter界面】

Load.insertLoadedTree(1, fhTree, false);

FileDialoger.setLastJFCDirectory(f.getParentFile().getAbsolutePath());
guiPackage.updateCurrentGui();
guiPackage.getMainFrame().repaint();

【七、结果验证】

【后记】

源码的解析,自定义代码,为后续其他扩展提供了可能。在自动生成了jmx脚本后,还可以继续通过代码执行脚本、生成报告等操作。当然由于本地资源的问题,可能出现outofmemory等问题,这时也可以考虑调整jvm参数,或进行分布式部署等方案。

Apache JMeter 源码(apache-jmeter-5.4.3_src.zip)可用于测试静态和动态资源、Web 动态应用程序的性能。 它可用于模拟服务器、服务器组、网络或对象上的重负载,以测试其强度或分析不同负载类型下的整体性能。 Apache JMeter 功能包括: 能够加载和性能测试许多不同的应用程序/服务器/协议类型: 网络 - HTTP、HTTPS(Java、NodeJS、PHP、ASP.NET,...) SOAP/REST 网络服务 FTP 通过 JDBC 数据库 LDAP 通过 JMS 的面向消息的中间件 (MOM) 邮件 - SMTP(S)、POP3(S) 和 IMAP(S) 本机命令或 shell 脚本 TCP Java 对象 全功能测试 IDE,允许快速记录测试计划(来自浏览器或本机应用程序)、构建和调试。 CLI 模式(命令行模式(以前​​称为非 GUI)/无头模式)从任何 Java 兼容操作系统(Linux、Windows、Mac OSX 等)加载测试 完整且可随时呈现的动态 HTML 报告 通过从最流行的响应格式、HTML、JSON、 XML或任何文本格式中提取数据的能力轻松关联 完全可移植性和100% Java 纯度。 完整的多线程框架允许多个线程并发采样和不同线程组同时采样不同功能。 缓存和离线分析/重放测试结果。 高度可扩展的核心: 可插拔采样器允许无限的测试功能。 可编写脚本的采样器(与 JSR223 兼容的语言,如Groovy和 BeanShell) 可以使用可插拔计时器选择多个负载统计信息。 数据分析和可视化插件允许极大的可扩展性和个性化。 函数可用于为测试提供动态输入或提供数据操作。 通过适用于 Maven、Gradle 和 Jenkins 的第3方开源库轻松实现持续集成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值