Appium+Selenium Grid并行测试ThreadLocal驱动为空问题求助
解决Appium ThreadLocal驱动为空的并行测试问题
问题根源分析
- 继承关系错误:测试类
_3_Onboarding继承的是_1_Appstart,但持有ThreadLocal驱动的是_2_Deviceinfo,导致测试方法无法访问到正确的驱动实例。 - 驱动创建顺序颠倒:在
_2_Deviceinfo的deviceSetUp方法中,先创建AndroidDriver再配置DesiredCapabilities,此时传入的cap是空的,无法初始化有效驱动,后续设置cap也不会生效。 - 直接访问driver变量:测试方法中直接使用
driver,没有通过getDriver()方法获取ThreadLocal中存储的线程隔离实例。
修正步骤与代码
1. 修正测试类的继承关系
让_3_Onboarding继承_2_Deviceinfo,确保能访问到ThreadLocal驱动的get/set方法:
package appiumset; import java.net.MalformedURLException; import org.testng.annotations.Test; import io.appium.java_client.MobileElement; public class _3_Onboarding extends _2_Deviceinfo { @Test public void onboarding() throws MalformedURLException, InterruptedException { System.out.println("_3_Onboarding Start"); // 通过getDriver()获取线程隔离的驱动实例 MobileElement arrow = (MobileElement) getDriver().findElementByAccessibilityId("next"); arrow.click(); System.out.println("next-done"); } }
2. 修正驱动初始化顺序
在_2_Deviceinfo的deviceSetUp方法中,先完成所有DesiredCapabilities的配置,再创建AndroidDriver并设置到ThreadLocal:
package appiumset; import java.net.MalformedURLException; import java.net.URL; import org.openqa.selenium.remote.DesiredCapabilities; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Parameters; import io.appium.java_client.AppiumDriver; import io.appium.java_client.android.AndroidDriver; import io.appium.java_client.remote.AndroidMobileCapabilityType; import io.appium.java_client.remote.MobileCapabilityType; public class _2_Deviceinfo { private ThreadLocal<AppiumDriver> driver = new ThreadLocal<>(); public void setDriver(AppiumDriver driver) { this.driver.set(driver); } public AppiumDriver getDriver() { return this.driver.get(); } @Parameters({"device", "apppackage", "activity","version","appiumServer" , "systemPort", "platformName"}) @BeforeMethod public void deviceSetUp(String device, String apppackage, String activity, String version, String appiumServer, String systemPort, String platformName) throws InterruptedException, MalformedURLException { System.out.println("****************************************"); System.out.println("Setting up device and desired capabilities"); DesiredCapabilities cap = new DesiredCapabilities(); URL url = new URL(appiumServer); // 先配置所有capabilities cap.setCapability(MobileCapabilityType.DEVICE_NAME, device); cap.setCapability(MobileCapabilityType.UDID, device); cap.setCapability(AndroidMobileCapabilityType.SYSTEM_PORT, systemPort); cap.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 120); cap.setCapability(MobileCapabilityType.PLATFORM_NAME, platformName); cap.setCapability(MobileCapabilityType.PLATFORM_VERSION, version); cap.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, apppackage); cap.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, activity); cap.setCapability("automationName", "UiAutomator2"); cap.setCapability("noReset", false); cap.setCapability("fullReset", true); cap.setCapability("appWaitActivity", "*"); cap.setCapability("autoWebview", false); // 配置完成后再创建驱动并设置到ThreadLocal setDriver(new AndroidDriver<>(url, cap)); // 可选:设置隐式等待 getDriver().manage().timeouts().implicitlyWait(10, java.util.concurrent.TimeUnit.SECONDS); } @AfterMethod public void closeDriver() { AppiumDriver driver = getDriver(); if (driver != null) { driver.quit(); driver.remove(); // 清理ThreadLocal中的实例,避免内存泄漏 } } }
额外注意事项
- 去掉
deviceSetUp方法的synchronized修饰符,并行测试时同步会导致线程阻塞,失去并行意义。 - ThreadLocal的实例建议用
private修饰,避免外部直接修改,只通过get/set方法操作。 - 在
AfterMethod中增加空判断,避免驱动已销毁时调用quit报错,同时调用driver.remove()清理ThreadLocal中的实例,防止内存泄漏。
内容的提问来源于stack exchange,提问作者M i
相关产品推荐
相关产品推荐

