通过Java代码运行JMeter测试生成匹配GUI的报告及空指针问题咨询
问题排查
本次出现空指针异常的核心原因如下:
- 测试计划的HashTree结构配置错误,采样器、线程组未正确关联到测试计划,导致压测未实际执行,结果文件无有效采样数据,生成报告时读取空数据触发空指针
- 未配置结果保存的全量必填字段,生成HTML报告需要的关键字段缺失
- 未配置报告生成的必填参数,且未等待JMeter执行完成、所有采样结果刷入文件就启动报告生成逻辑
- 结果文件误命名为
.html后缀,实际ReportGenerator仅接受CSV/JTL格式的结果文件作为输入
解决方案
按以下步骤修正代码即可生成和JMeter GUI一致的聚合图形、统计报告:
- 加载JMeter配置后补充结果保存、报告生成的必填参数
- 修正测试计划的HashTree层级结构,保证采样器、结果收集器正常生效
- 等待JMeter所有压测任务执行完成、结果完全落盘后再生成报告
- 给HTTP采样器设置名称,保证报告中采样标签正常展示
完整可运行代码
import org.apache.commons.io.FileUtils; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.control.LoopController; import org.apache.jmeter.engine.StandardJMeterEngine; import org.apache.jmeter.protocol.http.sampler.HTTPSampler; import org.apache.jmeter.reporters.ResultCollector; import org.apache.jmeter.reporters.Summariser; import org.apache.jmeter.report.dashboard.ReportGenerator; import org.apache.jmeter.testelement.TestPlan; import org.apache.jmeter.threads.ThreadGroup; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.ListedHashTree; import java.io.File; public class JMeterTest { public static void main(String[] args) throws Exception { StandardJMeterEngine jmeter = new StandardJMeterEngine(); // 替换为你本地的JMeter配置路径和Home路径 String jmeterHome = "bla/apache-jmeter-5.4.1"; String jmeterPropPath = "bla/jmeter.properties"; JMeterUtils.loadJMeterProperties(jmeterPropPath); JMeterUtils.setJMeterHome(jmeterHome); JMeterUtils.initLocale(); // ========== 新增:配置结果保存必填字段 ========== JMeterUtils.setProperty("jmeter.save.saveservice.output_format", "csv"); JMeterUtils.setProperty("jmeter.save.saveservice.assertion_results_failure_message", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.data_type", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.label", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.response_code", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.response_message", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.successful", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.thread_name", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.time", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.subresults", "false"); JMeterUtils.setProperty("jmeter.save.saveservice.assertions", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.latency", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.connect_time", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.sent_bytes", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.received_bytes", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.url", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.thread_counts", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.sample_count", "true"); JMeterUtils.setProperty("jmeter.save.saveservice.idle_time", "true"); // ========== 新增:配置报告生成必填参数 ========== String reportOutputDir = "report-output/dashboard"; JMeterUtils.setProperty("jmeter.reportgenerator.exporter.html.classname", "org.apache.jmeter.report.dashboard.HtmlTemplateExporter"); JMeterUtils.setProperty("jmeter.reportgenerator.exporter.html.property.output_dir", reportOutputDir); JMeterUtils.setProperty("jmeter.reportgenerator.template_directory", jmeterHome + "/report-template"); JMeterUtils.setProperty("jmeter.reportgenerator.overall_granularity", "1000"); JMeterUtils.setProperty("jmeter.reportgenerator.apdex_satisfied_threshold", "500"); JMeterUtils.setProperty("jmeter.reportgenerator.apdex_tolerated_threshold", "1500"); // 清理历史文件 File reportDir = new File(reportOutputDir); if (reportDir.exists()) { FileUtils.forceDelete(reportDir); } File resultFile = new File("result.csv"); if (resultFile.exists()) { FileUtils.forceDelete(resultFile); } SaveService.loadProperties(); // ========== 修正:HashTree层级结构 ========== HashTree testPlanTree = new ListedHashTree(); TestPlan testPlan = new TestPlan("压测计划"); testPlan.setUserDefinedVariables(new Arguments()); testPlanTree.add(testPlan); // 构造HTTP采样器 HTTPSampler httpSampler = new HTTPSampler(); httpSampler.setName("库存创建接口"); httpSampler.setDomain("host"); httpSampler.setPort(443); httpSampler.setPath("/api/v1/stock-count/created"); httpSampler.setMethod("POST"); httpSampler.setUseKeepAlive(true); // 构造循环控制器 LoopController loopController = new LoopController(); loopController.setLoops(1); loopController.setFirst(true); loopController.initialize(); // 构造线程组 ThreadGroup threadGroup = new ThreadGroup(); threadGroup.setName("压测线程组"); threadGroup.setNumThreads(10); threadGroup.setRampUp(1); threadGroup.setSamplerController(loopController); // 构造结果收集器 String summariserName = JMeterUtils.getPropDefault("summariser.name", "summary"); Summariser summer = new Summariser(summariserName); ResultCollector logger = new ResultCollector(summer); logger.setFilename(resultFile.getAbsolutePath()); // 按层级加入HashTree HashTree threadGroupTree = testPlanTree.add(testPlan, threadGroup); threadGroupTree.add(httpSampler, logger); // 执行压测 jmeter.configure(testPlanTree); jmeter.run(); // ========== 新增:等待压测完全结束、结果刷入文件 ========== while (jmeter.isRunning()) { Thread.sleep(1000); } Thread.sleep(2000); // 生成HTML报告 ReportGenerator generator = new ReportGenerator(resultFile.getAbsolutePath(), null); generator.generate(); System.out.println("报告生成完成,路径:" + reportOutputDir + "/index.html"); } }
验证
代码运行完成后,打开report-output/dashboard/index.html即可看到包含聚合统计、聚合图形、QPS曲线、响应时间分布等全套和JMeter GUI一致的可视化报告。
内容的提问来源于stack exchange,提问作者Serhii Kachan
相关产品推荐
相关产品推荐

