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

Playwright Java框架:@BeforeClass初始化Page后ITestResult属性全局访问问题

问题根源解析

问题出在**@BeforeClass阶段没有对应的ITestResult实例**。Reporter.getCurrentTestResult()仅在@Test方法执行期间会返回有效实例,而@BeforeClass属于测试类的初始化阶段,此时还未启动具体的测试方法,你拿到的itr其实是null,根本没完成Page的存储操作,所以监听器里调用getAttribute会返回null,进而触发空指针异常。


两种可行解决方案

方案1:用ThreadLocal存储Page(推荐,适配多线程测试)

ThreadLocal可以保证每个测试线程持有独立的Page实例,不管是在@BeforeClass、@BeforeTest还是@Test里初始化,监听器都能直接拿到当前线程的Page。

1. 修改封装方法

在你的封装类中定义ThreadLocal变量,并提供存取、清理方法:

// 定义线程局部变量存储Page
private static final ThreadLocal<Page> PAGE_THREAD_LOCAL = new ThreadLocal<>();

public BaseBrowser retrievePage(Browser browser, ThreadLocalContextMap contextMap, BrowserContext browserContext, String contextKey) {
    if (contextMap.containsKey(contextKey)) {
        browserContext = contextMap.get(contextKey);
    } else {
        browserContext = browser.newContext(new Browser.NewContextOptions().setIgnoreHTTPSErrors(true));
        contextMap.put(contextKey, browserContext);
    }
    Page scrnShtPg = browserContext.newPage();
    // 将Page存入ThreadLocal
    PAGE_THREAD_LOCAL.set(scrnShtPg);
    return new BaseBrowser(scrnShtPg);
}

// 供监听器获取当前线程的Page
public static Page getCurrentPage() {
    return PAGE_THREAD_LOCAL.get();
}

// 测试结束后清理ThreadLocal,避免内存泄漏
public static void removeCurrentPage() {
    PAGE_THREAD_LOCAL.remove();
}

2. 修改自定义监听器

@Override
public void onTestFailure(ITestResult iTestResult) {
    JsonObject element = null;
    byte[] bufferImage = null;

    // 从ThreadLocal获取当前Page
    Page page = YourWrapperClassName.getCurrentPage(); // 替换成你的封装类实际名称
    if (page != null) {
        bufferImage = page.screenshot();
    }

    try {
        element = SyscoLabReporting.getElement(iTestResult, bufferImage);
        elements.add(element);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    // 清理当前线程的Page实例
    YourWrapperClassName.removeCurrentPage();
}

3. 测试类无需修改

原@BeforeClass的初始化逻辑保持不变即可。


方案2:绑定Page到ITestContext(适合单线程测试)

@BeforeClass方法可以直接拿到ITestContext实例,把Page存到测试类级别的上下文属性中,监听器通过iTestResult.getTestContext()获取。

1. 修改测试类的setUp方法

@BeforeClass(alwaysRun = true)
public void setUp(ITestContext iTestContext) throws InterruptedException {
    page = getPage("Default");
    // 将Page存入ITestContext(假设BaseBrowser有方法返回原生Playwright Page实例)
    iTestContext.setAttribute("Page_Reference", page.getRawPage());
}

2. 修改自定义监听器

@Override
public void onTestFailure(ITestResult iTestResult) {
    JsonObject element = null;
    byte[] bufferImage = null;

    // 从测试上下文获取Page
    ITestContext testContext = iTestResult.getTestContext();
    Page page = (Page) testContext.getAttribute("Page_Reference");
    if (page != null) {
        bufferImage = page.screenshot();
    }

    try {
        element = SyscoLabReporting.getElement(iTestResult, bufferImage);
        elements.add(element);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

注意事项

此方案为测试类级别的上下文存储,多线程并行测试时会存在线程安全问题,仅适合单线程测试场景。


额外提示
  • 无论采用哪种方案,测试结束后务必关闭Page和BrowserContext,避免浏览器资源泄漏。
  • 如果在@Test方法内再次调用getPage(),ThreadLocal方案会自动维护当前线程的最新Page实例,无需额外处理。

内容的提问来源于stack exchange,提问作者Kasun Herath

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 23:27:07