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
相关产品推荐
相关产品推荐

