You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JMeter中使用Extent Report仅显示最后一个采样器结果问题求助

问题:JMeter集成ExtentReport 5.0.6仅显示最后一个采样器结果

我正在使用JMeter进行API功能自动化测试,尝试用ExtentReport 5.0.6生成测试报告,但报告结果被覆盖,仅显示最后一个执行的采样器。我的配置如下:

当前配置代码

每个ThreadGroup根节点的Beanshell Assertion代码

//request data
String requestData = new String(prev.SamplerData);
//String requestData = new String(requestData);
props.put("propRequestData", requestData);
//response data
String respData = new String(prev.ResponseData);
//String respData = new String(prev.getResponseDataAsString());
props.put("propResponse", respData);
//response code
String respCode = new String(prev.ResponseCode);
props.put("propRespCode",respCode);
//response message
String respMessage = new String(prev.ResponseMessage);
props.put("propRespMessage",respMessage);

测试计划根节点的JSR223 Listener代码

import com.aventstack.extentreports.*;
import com.aventstack.extentreports.reporter.*;
import com.aventstack.extentreports.markuputils.*;
import com.aventstack.extentreports.reporter.configuration.ViewName;

ExtentSparkReporter htmlReporter;
ExtentReports extent;
ExtentTest extentTest;
if(extent == null) {
// create the HtmlReporter
htmlReporter = new ExtentSparkReporter("C:/API-Results.html");

//configure report
htmlReporter.config().setOfflineMode(true);
//htmlReporter.config().setChartVisibilityOnOpen(true);
htmlReporter.config().setDocumentTitle("Results");
htmlReporter.config().setEncoding("utf-8");
htmlReporter.config().setReportName("CKPAT API Results");                                                                                                                                                   htmlReporter.viewConfigurer().viewOrder().as(new ViewName[] {ViewName.DASHBOARD, ViewName.TEST}).apply();

// create ExtentReports
extent = new ExtentReports();

// attach reporter to ExtentReports
extent.attachReporter(htmlReporter);
extent.setReportUsesManualConfiguration(true); 

// Show Env section and set data on dashboard
extent.setSystemInfo("Tool","JMeter");
extent.setSystemInfo("Environment", "QA");
} 

//extent.setSystemInfo("Test Time","${currentTime}");
//stringify test info
String threadName = sampler.getThreadName();
String samplerName = sampler.getName();
String requestData = props.get("propRequestData");
String respCode = props.get("propRespCode");
String respMessage = props.get("propRespMessage");
String responseData = props.get("propResponse");

// create test
extentTest = extent.createTest(threadName+" - "+samplerName);
//test.assignCategory("API Testing");

// analyze sampler result
if (vars.get("JMeterThread.last_sample_ok") == "false") {
   log.error("FAILED: "+samplerName);
   print("FAILED: "+samplerName);
   extentTest.fail(MarkupHelper.createLabel("FAILED:  "+sampler.getName(),ExtentColor.RED));

} else if (vars.get("JMeterThread.last_sample_ok") == "true") {
    if(responseData.contains("@error")) {
        log.info("FAILED: "+sampler.getName());
         print("FAILED: "+sampler.getName());
        extentTest.fail(MarkupHelper.createLabel("FAILED:   "+sampler.getName(),ExtentColor.RED));

    } else if (responseData.contains("{")) {
            log.info("Passed: "+sampler.getName());
        print("Passed: "+sampler.getName());
            extentTest.pass(MarkupHelper.createLabel("Passed:   "+sampler.getName(),ExtentColor.GREEN));
    }
} else {
    log.error("Something is really wonky");
    print("Something is really wonky");
    extentTest.fatal("Something is really wonky");
}

//info messages
    extentTest.info("RequestData:   "+requestData);
    extentTest.info("Response Code and Message:    "+respCode+"   "+respMessage);
    extentTest.info("ResponseData:    "+responseData);

extent.flush();
问题原因分析
  1. ExtentReports对象未持久化:JSR223 Listener中的extent是脚本局部变量,每次采样器触发监听器时,脚本重新执行,extent都会被重置为null,导致每次都重新创建ExtentReports对象,覆盖之前的报告文件。
  2. 全局props数据被覆盖:props是JMeter全局属性,每个采样器执行时都会用新数据覆盖同一个key的值,最终监听器只能获取到最后一个采样器的请求/响应数据。
  3. 频繁调用extent.flush():每个采样器后都执行flush,可能导致报告写入不完整,且加重IO负担。
解决方案

1. 持久化ExtentReports对象到全局属性

修改JSR223 Listener的初始化逻辑,将extent存入全局props,确保整个测试仅初始化一次:

import com.aventstack.extentreports.*;
import com.aventstack.extentreports.reporter.*;
import com.aventstack.extentreports.markuputils.*;
import com.aventstack.extentreports.reporter.configuration.ViewName;

ExtentSparkReporter htmlReporter;
ExtentReports extent;
ExtentTest extentTest;

// 从全局props获取已初始化的extent,不存在则创建
if (props.get("extent") == null) {
    htmlReporter = new ExtentSparkReporter("C:/API-Results.html");

    // 配置报告
    htmlReporter.config().setOfflineMode(true);
    htmlReporter.config().setDocumentTitle("Results");
    htmlReporter.config().setEncoding("utf-8");
    htmlReporter.config().setReportName("CKPAT API Results");                                                                                                                                       
    htmlReporter.viewConfigurer().viewOrder().as(new ViewName[] {ViewName.DASHBOARD, ViewName.TEST}).apply();

    extent = new ExtentReports();
    extent.attachReporter(htmlReporter);
    extent.setReportUsesManualConfiguration(true); 

    // 设置系统信息
    extent.setSystemInfo("Tool","JMeter");
    extent.setSystemInfo("Environment", "QA");
    
    // 存入全局props,避免重复初始化
    props.put("extent", extent);
} else {
    extent = props.get("extent");
}

2. 改用线程局部变量存储采样器数据

将Beanshell Assertion中的props替换为vars(线程局部变量,不会跨线程/采样器覆盖):

//request data
String requestData = new String(prev.SamplerData);
vars.put("propRequestData", requestData);
//response data
String respData = new String(prev.ResponseData);
vars.put("propResponse", respData);
//response code
String respCode = new String(prev.ResponseCode);
vars.put("propRespCode",respCode);
//response message
String respMessage = new String(prev.ResponseMessage);
vars.put("propRespMessage",respMessage);

同时修改JSR223 Listener中获取数据的方式:

// 获取采样器数据(改用vars)
String requestData = vars.get("propRequestData");
String respCode = vars.get("propRespCode");
String respMessage = vars.get("propRespMessage");
String responseData = vars.get("propResponse");

3. 调整extent.flush()执行时机

删除原JSR223 Listener中的extent.flush();语句,添加JSR223 Test Listener到测试计划根节点,在测试结束时统一执行flush:

import com.aventstack.extentreports.ExtentReports;

ExtentReports extent = props.get("extent");
if (extent != null) {
    extent.flush();
}

4. 优化结果判断逻辑

改用prev.isSuccessful()替代vars.get("JMeterThread.last_sample_ok"),逻辑更可靠:

// 分析采样器结果
if (!prev.isSuccessful()) {
    log.error("FAILED: " + samplerName);
    print("FAILED: " + samplerName);
    extentTest.fail(MarkupHelper.createLabel("FAILED:  " + sampler.getName(), ExtentColor.RED));
} else {
    if (responseData.contains("@error")) {
        log.info("FAILED: " + sampler.getName());
        print("FAILED: " + sampler.getName());
        extentTest.fail(MarkupHelper.createLabel("FAILED:   " + sampler.getName(), ExtentColor.RED));
    } else {
        log.info("Passed: " + sampler.getName());
        print("Passed: " + sampler.getName());
        extentTest.pass(MarkupHelper.createLabel("Passed:   " + sampler.getName(), ExtentColor.GREEN));
    }
}
验证修改

执行测试后,ExtentReport将包含所有采样器的执行结果,不会再出现覆盖问题。

内容的提问来源于stack exchange,提问作者Maithili Srinivasan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 04:40:55