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

Extent Report无法生成排查:POM框架自动化测试疑问

问题描述

我把单文件Selenium测试脚本重构为POM自动化框架后,TestNG套件和测试脚本能正常运行,但删除原有报告后无法生成新的Extent Report。下面是我的BaseTest类、SuiteListener类代码,请问是不是Listener实现有问题,或者是否需要添加ExtentManager/TestManager类?


BaseTest类代码

public class BaseTest {
    
//  Declare WebDriver and Extent Reports globally 
    
    public static WebDriver driver;
    ExtentReports extent = new ExtentReports();
    ExtentSparkReporter spark = new ExtentSparkReporter("target/results.html");
    ExtentTest logger;
    
    
    
//  Configure Extent Reporter to the test scripts 
    @BeforeTest
    public void beforeTestMethod() {
        
        spark = new ExtentSparkReporter("target/results.html");
        spark.config().setTheme(Theme.DARK);
        spark.config().setDocumentTitle("Selenium Report");
        spark.config().setReportName("Suite Report");
        extent.attachReporter(spark); 
        extent = new ExtentReports();
        
    }
    
//  Set driver to the specific driver stated in the TESTNG test suite & configure window/ wait 
    @BeforeMethod
    @Parameters("browser")
    public void beforeMethod(String browser, Method testMethod) {
        logger = extent.createTest(testMethod.getName());
        setupDriver(browser);
        driver.manage().window().maximize();
        driver.get(utils.Constants.url);
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
    }
    
    public void setupDriver(String browser) {
        if(browser.equalsIgnoreCase("chrome")) {
            WebDriverManager.chromedriver().setup();
            driver = new ChromeDriver();
        }
        else if(browser.equalsIgnoreCase("firefox")) {
            WebDriverManager.firefoxdriver().setup();
            driver = new FirefoxDriver();
        }
        else if(browser.equalsIgnoreCase("edge")) {
            WebDriverManager.edgedriver().setup();
            driver = new EdgeDriver();
        }
        
        
    }
    
//  Add a logger for test results 
    @AfterMethod
    public void afterMethod(ITestResult result) {
        if(result.getStatus() == ITestResult.FAILURE) {
            logger.log(Status.FAIL, MarkupHelper.createLabel(result.getName() + " - Test Case Failed", ExtentColor.RED));
            logger.log(Status.FAIL, MarkupHelper.createLabel(result.getThrowable() + " - Test Case Failed", ExtentColor.RED));          
        }
        else if(result.getStatus() == ITestResult.SKIP) {
            logger.log(Status.SKIP, MarkupHelper.createLabel(result.getName() + " - Test Case Skipped", ExtentColor.ORANGE));
        }
        else if(result.getStatus() == ITestResult.SUCCESS) {
            logger.log(Status.PASS, MarkupHelper.createLabel(result.getName() + " - Test Case Passed", ExtentColor.GREEN));

        }
    }
    
//  Results are sent through the extent report with this method 
    @AfterTest
    public void afterTest() {
        extent.flush();
        driver.quit();
        
    }
    
}

SuiteListener类代码

public class SuiteListener implements ITestListener, IAnnotationTransformer {
    
    
//  Configure a test failure method to retry the script and send screenshots for the failed test 
     public void onTestFailure(ITestResult result) {
            String filename = System.getProperty("user.dir")+File.separator+"screenshots"+File.separator+result.getMethod().getMethodName();
            File f1 = ((TakesScreenshot)BaseTest.driver).getScreenshotAs(OutputType.FILE);
            
            try {
                FileUtils.copyFile(f1, new File(filename+ ".png"));
            }
            catch (IOException e) {
                e.printStackTrace();
            }
          }
     
//   Configure retry method 
     public void transform(
              ITestAnnotation annotation, Method testMethod) {
            annotation.setRetryAnalyzer(RetryAnalyzer.class);
          }

}

原单文件测试脚本(可正常生成报告)

public class FirstTest {
    
    WebDriver driver;
    ExtentReports extent = new ExtentReports();
    ExtentSparkReporter spark = new ExtentSparkReporter("target/results.html");
    ExtentTest test1, test2;
    
    @BeforeSuite
    public void setUpTest() {
        
         driver = new EdgeDriver();
         driver.manage().window().maximize();
         spark.config().setTheme(Theme.DARK);
         spark.config().setDocumentTitle("Selenium Report");
         extent.attachReporter(spark);
         
    }

    @Test
    public void Test() {
         test1 = extent.createTest("Login to SMB").assignAuthor("Jacob").
                assignCategory("Functional Test Case").assignDevice("Windows X64");
                
        try {
            test1.info("Get Login Page");
            driver.get("randomwebsite.com");
            
            driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
            
            test1.info("Find Email Text-box and input email");
            WebElement username = driver.findElement(By.cssSelector("#signin-email"));
            username.click();
            username.sendKeys("fakeUser");
            
            test1.info("Find Password textbox and input password.");
            WebElement password = driver.findElement(By.cssSelector("#signin-password"));
            password.click();
            password.sendKeys("password");
            
            test1.info("Clicking Login button");
            WebElement button = driver.findElement(By.cssSelector("#btn-login"));
            button.click();
            
            test1.log(Status.PASS, "Successfully logged in");
            
        } catch (Exception ex){
            test1.log(Status.FAIL, ex);
            throw ex;
        }
        
    }
    
    @Test
    public void Test2() {
         test2 = extent.createTest("Navigate to My Invoices Page").assignAuthor("Jacob").
                assignCategory("Functional Test Case").assignDevice("Windows X64");
         
         try {
             test2.info("Find Invoice dropdown menu");
             WebElement invoiceButton = driver.findElement(By.cssSelector("#outlined/archive"));
             invoiceButton.click();
             
             test2.info("Select My Invoices in the dropdown");
             WebElement dropdown = driver.findElement(By.id("dropdown"));
             dropdown.click();
             
             test2.log(Status.PASS, "Sucessfully loaded the Page");
             
         } catch (Exception ex) {
             test2.log(Status.FAIL, ex);
             throw ex;
         };
                
    } 
    
    @AfterSuite 
    public void EndTest() {
        extent.flush();
        driver.quit();
        
    }

}

问题分析与修复方案

1. 核心错误:BaseTest中的Extent实例被重置

在beforeTestMethod()方法中,你先初始化并配置了spark,将其附加到extent后,又执行了extent = new ExtentReports();,这直接丢弃了已经绑定报告配置的实例,后续所有测试日志都无法写入报告。

另外,extent、spark、logger应该设为静态成员,否则每个测试类继承BaseTest时都会创建新的实例,导致报告无法正确聚合。

2. 修复后的BaseTest代码

public class BaseTest {
    
    // 声明为静态成员,确保全局唯一实例
    public static WebDriver driver;
    public static ExtentReports extent;
    public static ExtentSparkReporter spark;
    public static ExtentTest logger;
    
    // 只在套件启动时初始化一次Extent Report
    @BeforeSuite
    public void beforeSuite() {
        spark = new ExtentSparkReporter("target/results.html");
        spark.config().setTheme(Theme.DARK);
        spark.config().setDocumentTitle("Selenium Report");
        spark.config().setReportName("Suite Report");
        extent = new ExtentReports();
        extent.attachReporter(spark); 
    }
    
    @BeforeMethod
    @Parameters("browser")
    public void beforeMethod(String browser, Method testMethod) {
        logger = extent.createTest(testMethod.getName());
        setupDriver(browser);
        driver.manage().window().maximize();
        driver.get(utils.Constants.url);
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
    }
    
    public void setupDriver(String browser) {
        if(browser.equalsIgnoreCase("chrome")) {
            WebDriverManager.chromedriver().setup();
            driver = new ChromeDriver();
        } else if(browser.equalsIgnoreCase("firefox")) {
            WebDriverManager.firefoxdriver().setup();
            driver = new FirefoxDriver();
        } else if(browser.equalsIgnoreCase("edge")) {
            WebDriverManager.edgedriver().setup();
            driver = new EdgeDriver();
        }
    }
    
    @AfterMethod
    public void afterMethod(ITestResult result) {
        if(result.getStatus() == ITestResult.FAILURE) {
            logger.log(Status.FAIL, MarkupHelper.createLabel(result.getName() + " - 测试用例失败", ExtentColor.RED));
            logger.log(Status.FAIL, MarkupHelper.createLabel(result.getThrowable() + " - 失败原因", ExtentColor.RED));          
        } else if(result.getStatus() == ITestResult.SKIP) {
            logger.log(Status.SKIP, MarkupHelper.createLabel(result.getName() + " - 测试用例跳过", ExtentColor.ORANGE));
        } else if(result.getStatus() == ITestResult.SUCCESS) {
            logger.log(Status.PASS, MarkupHelper.createLabel(result.getName() + " - 测试用例通过", ExtentColor.GREEN));
        }
    }
    
    @AfterSuite
    public void afterSuite() {
        extent.flush();
        driver.quit();
    }
    
}

3. SuiteListener优化(可选)

当前Listener的截图逻辑可以保留,建议把截图嵌入到Extent Report中,而非仅保存本地:

public void onTestFailure(ITestResult result) {
    String filename = System.getProperty("user.dir")+File.separator+"screenshots"+File.separator+result.getMethod().getMethodName();
    File f1 = ((TakesScreenshot)BaseTest.driver).getScreenshotAs(OutputType.FILE);
    
    try {
        FileUtils.copyFile(f1, new File(filename+ ".png"));
        // 嵌入截图到报告
        String base64Screenshot = ((TakesScreenshot)BaseTest.driver).getScreenshotAs(OutputType.BASE64);
        BaseTest.logger.addScreenCaptureFromBase64String(base64Screenshot, result.getName() + " 失败截图");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

4. 是否需要ExtentManager/TestManager?

中小型项目修复BaseTest的静态实例问题后足够使用;大型项目建议封装ExtentManager单例类管理ExtentReports实例,避免多线程/多测试类冲突,示例:

public class ExtentManager {
    private static ExtentReports extent;
    
    public static ExtentReports getInstance() {
        if(extent == null) {
            ExtentSparkReporter spark = new ExtentSparkReporter("target/results.html");
            spark.config().setTheme(Theme.DARK);
            spark.config().setDocumentTitle("Selenium Report");
            spark.config().setReportName("Suite Report");
            extent = new ExtentReports();
            extent.attachReporter(spark);
        }
        return extent;
    }
}

之后在BaseTest中用extent = ExtentManager.getInstance();获取实例即可。


内容的提问来源于stack exchange,提问作者Jacob Ehrler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 13:17:35