欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > 性能工具之 HAR 格式化转换JMeter JMX 脚本文件

性能工具之 HAR 格式化转换JMeter JMX 脚本文件

2024/10/23 23:28:26 来源:https://blog.csdn.net/zuozewei/article/details/142936071  浏览:    关键词:性能工具之 HAR 格式化转换JMeter JMX 脚本文件

文章目录

  • 一、前言
  • 二、什么是HAR文件?
  • 三、参考代码
  • 四、操作步骤

一、前言

最近要做一个系统的性能测试,菜单多,相对接口应该也比较多,想一想能否通过har格式转换成一个脚本呢?网上有不少案例,找了几个发现都没有成功,后面想了想还是自己搞定。

二、什么是HAR文件?

HAR(HTTP Archive format),是一种或 JSON 格式的存档格式文件,通用扩展名为 .har。Web 浏览器可以使用该格式导出有关其加载的网页的详细性能数据。

三、参考代码

代码主要实现以下功能:

  1. 配置JMeter路径
  2. 读取HAR文件
  3. 解析HAR文件
  4. 构建JMX文件
  5. 输出JMX 脚本
// 设置jmeterHome路径// 主要是读取了几个配置文件,jmeter.properties,user.properties,system.properties。// 设置一下的本地的Locale环境。// 其实到这里,是可以仅将这3个配置文件抽离出来,即不需要整个Jmeter的home目录,仅要这3个配置文件就能运行Jmeter脚本。// 甚至仅在代码中写要的配置,都不需要实体的配置文件即可。// 当然随着功能越来越多,平台跟Jmeter的耦合也越来越多,这个Jmeter_home目录还是越来越必要了。public static void main(String[] args) {// jmeter 路径File jmeterHome = new File("D:\\Program Files\\apache-jmeter-5.6.3");// 分隔符String slash = System.getProperty("file.separator");//判断jmeterHomeif (jmeterHome.exists()) {File jmeterProperties = new File(jmeterHome.getPath() + slash + "bin" + slash + "jmeter.properties");if (jmeterProperties.exists()) {// 初始化压测引擎StandardJMeterEngine jmeter = new StandardJMeterEngine();// JMeter初始化(属性、日志级别、区域设置等)JMeterUtils.setJMeterHome(jmeterHome.getPath());JMeterUtils.loadJMeterProperties(jmeterProperties.getPath());// 可以注释这一行,查看额外的日志,例如DEBUG级别JMeterUtils.initLogging();JMeterUtils.initLocale();// JMeter测试计划,基本上是JOrphan HashTreeHashTree testPlanTree = new HashTree();String Name = "baidu-jmx";// har 路径FileReader fileReader = new FileReader("aa".har");String result = fileReader.readString();JSONObject harJson = JSONObject.parseObject(result);// 获取 HAR 文件中的 entriesJSONObject log = harJson.getJSONObject("log");JSONArray entries = log.getJSONArray("entries");// Loop Controller 循环控制LoopController loopController = getLoopController();// Thread Group 线程组ThreadGroup threadGroup = getThreadGroup(Name, loopController);// Test Plan 测试计划TestPlan testPlan = getTestPlan();// 从以上初始化的元素构造测试计划testPlanTree.add(testPlan);HashTree threadGroupHashTree = testPlanTree.add(testPlan, threadGroup);// 构建事务TransactionController transactionController = getTransactionController();HashTree transactionControllerHashTree = threadGroupHashTree.add(transactionController);entries.forEach(item -> {JSONObject json = (JSONObject) item;JSONObject request = json.getJSONObject("request");String method = request.getString("method");String url = request.getString("url");HTTPSamplerProxy Sampler = new HTTPSamplerProxy();buildTheUnderlyingData(url, Sampler, method);Sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());Sampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName());// 头信息HeaderManager headerManager = new HeaderManager();JSONArray headersList = request.getJSONArray("headers");headersList.forEach(header -> {JSONObject jsonObject = (JSONObject) header;Header headers = new Header();headers.setName(jsonObject.getString("name"));headers.setValue(jsonObject.getString("value"));headerManager.add(headers);});headerManager.setComment("");headerManager.setName("header Manager");headerManager.setProperty(TestElement.TEST_CLASS, HeaderManager.class.getName());headerManager.setProperty(TestElement.GUI_CLASS, HeaderPanel.class.getName());// 构建参数buildParameters(method, headersList, Sampler, request);// 处理断言ResponseAssertion assertion = getResponseAssertion();// 构造新的树transactionControllerHashTree.add(Sampler, headerManager);transactionControllerHashTree.add(Sampler, assertion);});try {// 将生成的测试计划保存为JMeter的.jmx文件格式String jmxFilePath = "D:\\work\\jmx\\aaa.jmx";SaveService.saveTree(testPlanTree, Files.newOutputStream(Paths.get(jmxFilePath)));} catch (IOException e) {throw new RuntimeException(e);}}}}/*** 根据给定的URL配置 HTTPSamplerProxy 对象* 该方法用于解析URL并配置HTTPSamplerProxy对象的相关参数,以便进行HTTP请求*  buildTheUnderlyingData* @param url      要请求的URL地址* @param Sampler  HTTPSamplerProxy对象,用于发送HTTP请求* @param method   HTTP请求方法,如GET、POST等* @throws RuntimeException 如果URL格式错误,则通过抛出运行时异常来处理*/private static void buildTheUnderlyingData(String url, HTTPSamplerProxy Sampler, String method) {String path;int port = 80;String domain = "";try {// 解析URLURL urlPath = new URL(url);// 获取URL的路径path = urlPath.getPath();// 获取URL的域名domain = urlPath.getHost();// 获取URL的端口,如果未指定则默认为-1port = urlPath.getPort();// 获取并设置URL的协议,如http、https等String protocol = urlPath.getProtocol();Sampler.setProtocol(protocol);// 根据URL的端口设置Sampler的端口,默认为80if (port == -1) {Sampler.setPort(port);} else {Sampler.setPort(80);}// 设置Sampler的域名、路径和请求方法Sampler.setDomain(domain);Sampler.setPath(path);Sampler.setMethod(method);} catch (MalformedURLException e) {// 如果URL格式不正确,抛出运行时异常throw new RuntimeException(e);}// 设置Sampler的名称为URL的路径Sampler.setName(path);}/*** 构建HTTP请求参数* 根据请求方法、请求头和请求体的不同情况,设置HTTP请求的参数* 目前支持GET、POST请求方法,DELETE和PUT方法留作未来扩展** @param method 请求方法,如"GET"、"POST"、"DELETE"、"PUT"* @param headersList 请求头的JSONArray,用于判断内容类型* @param Sampler HTTPSamplerProxy对象,用于添加请求参数* @param request 请求的JSONObject,包含请求数据*/private static void buildParameters(String method, JSONArray headersList, HTTPSamplerProxy Sampler, JSONObject request) {// 处理POST请求if (method.equals("POST")) {// 过滤出内容类型为application/json的请求头List<JSONObject> filteredObjects = filterJsonObjectsByValue(headersList, "application/json");// 如果存在application/json类型的请求头,设置请求体为原始格式,并设置请求体内容if (!filteredObjects.isEmpty()) {Sampler.setPostBodyRaw(true);JSONObject postData = request.getJSONObject("postData");Sampler.addArgument("", postData.getString("text"), "=");}// 过滤出内容类型为application/x-www-form-urlencoded的请求头filteredObjects = filterJsonObjectsByValue(headersList, "application/x-www-form-urlencoded");// 如果存在application/x-www-form-urlencoded类型的请求头,设置请求体为非原始格式,并添加查询字符串参数if (!filteredObjects.isEmpty()) {Sampler.setPostBodyRaw(false);JSONArray queryString = request.getJSONArray("queryString");queryString.forEach(item1 -> {JSONObject jsonObject = (JSONObject) item1;Sampler.addArgument(jsonObject.getString("name"), jsonObject.getString("value"), "=");});}}// 处理GET请求else if (method.equals("GET")) {// 设置请求体为非原始格式,并添加查询字符串参数Sampler.setPostBodyRaw(false);JSONArray queryString = request.getJSONArray("queryString");queryString.forEach(item -> {JSONObject jsonObject = (JSONObject) item;Sampler.addArgument(jsonObject.getString("name"), jsonObject.getString("value"), "=");});}// 处理DELETE请求,目前未实现具体逻辑else if (method.equals("DELETE")) {// todo 根据实际情况处理}// 处理PUT请求,目前未实现具体逻辑else if (method.equals("PUT")) {// todo 根据实际情况处理}}/*** 获取响应断言对象* <p>* 此方法用于创建并配置一个ResponseAssertion对象,主要用于测试过程中对响应内容进行断言* 它设置了断言的基本属性,并配置它以检查响应数据中是否存在特定的内容** @return ResponseAssertion 返回配置好的ResponseAssertion对象,用于后续的HTTP响应断言*/private static @NotNull ResponseAssertion getResponseAssertion() {// 创建一个响应断言实例ResponseAssertion assertion = new ResponseAssertion();// 设置测试字段的响应数据assertion.setTestFieldResponseData();// 设置不默认假设成功assertion.setAssumeSuccess(false);// 定义断言内容String assertContent = "success\":";// 设置测试类和GUI类的属性assertion.setProperty(TestElement.TEST_CLASS, ResponseAssertion.class.getName());assertion.setProperty(TestElement.GUI_CLASS, AssertionGui.class.getName());// 设置断言的名称assertion.setName("Response Assertion");// 如果断言内容非空,添加到断言中if (Objects.nonNull(assertContent)) {assertion.addTestString(assertContent.trim());}// 设置断言类型为包含测试assertion.setToContainsType();// 返回配置好的断言对象return assertion;}/*** 获取一个配置好的TransactionController实例** 该方法负责创建并配置一个TransactionController对象,用于后续的事务控制* TransactionController是用于在性能测试中管理事务的重要类,它可以控制事务的开始和结束,* 以及是否包含计时器等属性的设置** @return TransactionController 返回一个配置好的TransactionController实例,用于在测试中控制事务*/private static @NotNull TransactionController getTransactionController() {// 创建一个新的TransactionController实例TransactionController transactionController = new TransactionController();// 设置TransactionController的名称transactionController.setName("Transaction Controller");// 设置测试类名为TransactionController类的名称transactionController.setProperty(TestElement.TEST_CLASS, TransactionController.class.getName());// 设置GUI类名为TransactionControllerGui类的名称transactionController.setProperty(TestElement.GUI_CLASS, TransactionControllerGui.class.getName());// 设置生成父样本的标志,以便在采样时包含父事务的信息transactionController.setGenerateParentSample(true);// 设置不包含计时器,即事务中不包含计时操作transactionController.setIncludeTimers(false);// 返回配置好的TransactionController实例return transactionController;}/*** 创建并返回一个JMeter测试计划* 测试计划用于组织和管理JMeter测试的所有方面** @return 返回一个已经初始化的TestPlan实例,从未返回null*/private static @NotNull TestPlan getTestPlan() {// 创建一个新的测试计划,命名为"创建JMeter脚本"TestPlan testPlan = new TestPlan("创建JMeter脚本");// 设置测试元素的测试类属性,为其提供类名// 这是JMeter内部用来识别和分类测试元素的一种机制testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName());// 设置测试元素的GUI类属性,为其提供对应的GUI类名// 这影响了在图形界面模式下测试元素如何被呈现testPlan.setProperty(TestElement.GUI_CLASS, TestPlanGui.class.getName());// 设置用户定义的变量// 这里使用ArgumentsPanel创建一个TestElement,并将其转换为Arguments类型,以便设置用户变量// Arguments是JMeter中用于管理用户定义变量的类testPlan.setUserDefinedVariables((Arguments) new ArgumentsPanel().createTestElement());// 返回初始化完成的测试计划对象return testPlan;}/*** 根据给定的线程名称和循环控制器创建并返回一个新的线程组* 此方法用于初始化一个线程组实例,设置其基本属性,如名称、线程数、预热步骤和采样控制器** @param Name 线程组的名称,用于标识该线程组* @param loopController 控制线程循环的控制器,决定线程如何循环执行任务* @return 返回一个初始化完毕的、不可为空的线程组实例*/private static @NotNull ThreadGroup getThreadGroup(String Name, LoopController loopController) {// 创建一个新的线程组实例ThreadGroup threadGroup = new ThreadGroup();// 设置线程组的名称threadGroup.setName(Name);// 设置线程组中的线程数量为1threadGroup.setNumThreads(1);// 设置预热步骤为1,通常用于性能测试前的预热threadGroup.setRampUp(1);// 设置采样控制器,控制线程的循环行为threadGroup.setSamplerController(loopController);// 设置线程组的类名属性,用于测试元素的分类threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName());// 设置线程组的GUI类名属性,用于图形用户界面的关联threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName());// 返回配置完毕的线程组实例return threadGroup;}/*** 获取循环控制器实例** 该方法用于创建并初始化一个LoopController实例,用于控制测试循环的行为* 循环控制器在性能测试中扮演重要角色,它定义了测试用例如何循环执行** @return 返回一个已经初始化的LoopController实例,保证不会返回null*/private static @NotNull LoopController getLoopController() {// 创建一个新的循环控制器实例LoopController loopController = new LoopController();// 设置循环次数为1,意味着测试用例将被执行一次loopController.setLoops(1);// 将第一个循环标志设置为true// 这个标志用于指示当前的循环是否是第一次循环,在某些逻辑中可能会用到这个信息loopController.setFirst(true);// 设置循环控制器的测试类属性为其自身的类名// 这是为了在JMeter的GUI中正确识别该元素的类型loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());// 设置循环控制器的GUI类属性为其对应的GUI组件类名// 这是为了在JMeter的GUI中显示相应的配置面板loopController.setProperty(TestElement.GUI_CLASS, LoopControlPanel.class.getName());// 初始化循环控制器// 初始化步骤是必要的,确保循环控制器能够正确地开始工作loopController.initialize();// 返回已经配置和初始化的循环控制器实例return loopController;}/*** 过滤JSON对象数组中的值** 该方法通过搜索字符串来过滤一个JSON对象数组,返回值中包含搜索字符串的所有JSON对象** @param jsonArray JSON对象数组,其中每个对象都包含一个"key"-"value"对* @param searchString 搜索字符串,用于过滤JSON对象* @return 包含搜索字符串的所有JSON对象的列表*/public static List<JSONObject> filterJsonObjectsByValue(JSONArray jsonArray, String searchString) {// 创建一个列表,用于存储过滤后的JSON对象List<JSONObject> filteredObjects = new ArrayList<>();// 遍历JSON对象数组for (int i = 0; i < jsonArray.size(); i++) {// 获取当前迭代的JSON对象JSONObject jsonObject = jsonArray.getJSONObject(i);// 获取JSON对象中的"value"字段字符串String value = jsonObject.getString("value");// 如果"value"字段字符串包含搜索字符串,则将该JSON对象添加到过滤列表中if (value.contains(searchString)) {filteredObjects.add(jsonObject);}}// 返回过滤后的JSON对象列表return filteredObjects;}

四、操作步骤

上面代码写完毕后,打开"浏览器开启开发者模式",点击<Fetch/XHR>:
在这里插入图片描述

保存上面数据:
在这里插入图片描述

执行上面脚本:
在这里插入图片描述

修改执行保存路径:
在这里插入图片描述

打开存储路,查看文件:
在这里插入图片描述

打开JMeter:
在这里插入图片描述

添加结果查看树,并且执行该脚本:
在这里插入图片描述

执行正确,为什么会是红色显示呢,大家看上面代码在看看断言就知道是上面原因呢?
在这里插入图片描述
通过上面步骤就能构建一个完整的 JMX 文件。

如果想通过内置StandardJMeterEngine 执行该脚本。

可以参考如下:

  • 性能工具之JMeter两个Java API Demo

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com