TestNG+Cucumber+Appium并行测试PageFactory初始化报错问题
问题现象
基于TestNG+Cucumber+Appium搭建并行执行测试框架时,第二个运行线程抛出org.openqa.selenium.NoSuchSessionException: Session ID is null异常:
- 使用BaseMobile中Method1的静态方法初始化PageObject时稳定复现报错
- 改用构造函数初始化(Method2)时用例可正常运行
根因定位
三个核心问题导致并行场景下报错:
- 静态Driver破坏ThreadLocal线程隔离
BaseMobile中定义的protected static AppiumDriver driver = new AndroidDeviceA().getDriver();是类级别的静态变量,在类第一次加载时就会执行初始化:此时测试线程还未执行@BeforeClass中的driver初始化逻辑,ThreadLocal中存储的driver为null;同时静态变量被所有线程共享,并行执行时后启动的线程会覆盖该变量值,先启动的线程会拿到其他线程已quit的driver实例,直接触发无会话异常。 - PageFactory初始化方式不符合Appium要求
Method1直接调用PageFactory.initElements(driver, clazz)传入静态driver,没有使用Appium提供的AppiumFieldDecorator做装饰,无法正确识别Appium的移动端元素注解(如@AndroidFindBy),元素代理绑定的driver实例本身就是错误的。 - Driver初始化判空逻辑无效
AndroidDeviceA.initializeDriver()方法中新建了局部变量AppiumDriver driver = null;做判空,永远不会命中ThreadLocal中已存储的当前线程driver,虽然不是本次报错的直接原因,但并行场景下存在重复初始化driver的风险。
修复方案
- 删除
BaseMobile中的静态driver变量,所有driver实例统一从AndroidDeviceA的ThreadLocal中获取,保证线程隔离 - 修正静态Page初始化方法,使用
AppiumFieldDecorator绑定当前线程的实时driver实例,和Method2的初始化逻辑对齐 - 修正driver初始化的判空逻辑,直接判断当前线程ThreadLocal中存储的driver是否存在,避免重复初始化
修正后的核心代码:
BaseMobile修正后代码
public class BaseMobile { TestUtils utils = new TestUtils(); public final static Logger logger = LogManager.getLogger(); // 实时获取当前线程绑定的driver,禁止用静态变量存储driver protected AppiumDriver getDriver() { return new AndroidDeviceA().getDriver(); } // 修正后的Method1,对齐Method2的初始化逻辑 public static <T extends BaseMobile> T instanceOf(Class<T> clazz) { return PageFactory.initElements( new AppiumFieldDecorator(new AndroidDeviceA().getDriver()), clazz ); } }
AndroidDeviceA初始化逻辑修正
public void initializeDriver() throws Exception { // 直接判断当前线程绑定的driver,不要定义局部变量做无效判空 if (driver.get() == null) { try { utils.log().info("initializing Appium driver"); AppiumDriver driverInstance = new AndroidDriver(new AppiumServer().getServer().getUrl(), new UiAutomatorOptions().getUiAutomatorOptionsMobile()); if (driverInstance == null) { throw new Exception("driver is null. ABORT!!!"); } utils.log().info("Driver is initialized"); utils.log().info("session"+ driverInstance.getSessionId()); driver.set(driverInstance); } catch (IOException e) { e.printStackTrace(); utils.log().fatal("Driver initialization failure. ABORT !!!!" + e.toString()); throw e; } } }
Method2正常运行的原因
Method2在实例化Page对象时才执行初始化:
- 此时
@BeforeClass逻辑已经执行完成,当前线程的driver已经完成初始化并存入ThreadLocal,能拿到正确的会话实例 - 初始化时使用了
AppiumFieldDecorator装饰driver,元素代理绑定的是当前线程的正确driver,不会出现跨线程会话错乱的问题
内容的提问来源于stack exchange,提问作者jordan99
相关产品推荐
相关产品推荐

