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

TestNG+Selenium方法级并行测试WebDriver空指针问题排查

问题产生原因

空指针异常的核心原因是ThreadLocal的初始化位置错误:

  1. 代码中将ThreadLocal<WebDriver>声明为类的普通成员变量,且在@BeforeMethod注解的setup方法中每次执行都重新new一个新的ThreadLocal实例赋值给this.driver
  2. 两个测试线程并行执行时,线程1先进入setup方法,创建ThreadLocalA并存入自己的ChromeDriver实例;随后线程2进入setup方法,直接把类实例中driver的引用替换为全新的空ThreadLocalB。此时线程1持有的driver引用已经变成了没有存任何值的ThreadLocalB,调用get()方法自然返回null,触发空指针。

至于不使用ThreadLocal时出现的操作串浏览器问题,本质是WebDriver本身不具备线程安全性,多个测试线程共享同一个WebDriver实例、或者共享同一个存储WebDriver的普通成员变量时,后启动的线程会覆盖之前线程存入的driver引用,最终所有线程的操作都会落到最后一个初始化的浏览器窗口上。

修复方案

只需要调整ThreadLocal的初始化位置,补充资源清理逻辑即可,具体修改点:

  • 将ThreadLocal<WebDriver>的初始化提前到类加载阶段,声明为static final类型,禁止在setup方法中重复实例化ThreadLocal
  • 在@BeforeMethod中仅做WebDriver实例的创建和ThreadLocal的set操作,不重新创建ThreadLocal对象
  • 新增@AfterMethod注解的清理方法,测试执行完成后关闭当前线程对应的浏览器,同时调用ThreadLocal的remove()方法清理引用,避免内存泄漏

修复后的完整可运行代码如下:

import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.time.Duration;

public class LoginTest {
    // 类加载时就初始化ThreadLocal,全局唯一,禁止重复实例化
    private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();

    @BeforeMethod()
    public void setup() {
        WebDriverManager.chromedriver().setup();
        WebDriver webDriver = new ChromeDriver();
        webDriver.manage().window().maximize();
        webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        webDriver.get("https://www.saucedemo.com/");
        // 仅往已初始化的ThreadLocal中存入当前线程的driver实例
        driver.set(webDriver);
    }

    @Test(invocationCount = 2, threadPoolSize = 2)
    public void test() {
        driver.get().findElement(By.id("user-name")).sendKeys("standard_user");
        driver.get().findElement(By.id("password")).sendKeys("secret_sauce");
        // 原示例代码仅定位了登录按钮未点击,补充点击动作
        driver.get().findElement(By.id("login-button")).click();
    }

    @AfterMethod
    public void teardown() {
        WebDriver currentDriver = driver.get();
        if (currentDriver != null) {
            // 关闭当前线程对应的浏览器
            currentDriver.quit();
            // 清理ThreadLocal中的引用,避免线程复用、内存泄漏问题
            driver.remove();
        }
    }
}
方法级并行测试下WebDriver的标准使用规范
  • 强制线程隔离:WebDriver是非线程安全对象,必须保证每个测试线程持有独立的WebDriver实例,绝对禁止多线程共享同一个WebDriver实例
  • ThreadLocal正确初始化:存储WebDriver的ThreadLocal必须声明为static final类型,在类加载阶段完成初始化,禁止在@BeforeMethod/@BeforeTest等生命周期方法中重复创建ThreadLocal实例,避免引用覆盖导致的取值为空问题
  • 生命周期严格对齐:WebDriver实例的初始化放在@BeforeMethod中执行,保证每个测试方法运行前拿到全新的浏览器实例;测试方法执行完成后,在@AfterMethod中关闭浏览器、清理ThreadLocal存储的引用,避免出现脏数据、内存泄漏问题
  • 避免使用普通成员变量存储WebDriver:普通成员变量在多线程访问类实例时会出现写覆盖,直接导致不同测试线程的操作互相干扰。

内容的提问来源于stack exchange,提问作者Katerina Zharkaya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:48:25