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

使用Appium+TestNG+BrowserStack时Page Object报空指针异常

问题:构造Page Object时抛出NullPointerException

在使用BrowserStack+Appium+TestNG编写自动化测试并引入Page Object模式时,构造页面对象时持续出现NullPointerException,报错行为LandingPage landingPage = new LandingPage(appiumDriver);

相关代码

BrowserStackConfig配置类

public class BrowserStackConfig {
    protected AppiumDriver<MobileElement> appiumDriver;

    @BeforeMethod(alwaysRun = true)
    @Parameters("platform")
    public void setUp(String platform, ITestResult testMethod) throws Exception {
        Map<String, Object> config = readConfigFromFile();
        DesiredCapabilities capabilities = new DesiredCapabilities();
        ConfigReader reader = new ConfigReader();

        String device;
        String os;
        String app;

        switch (platform) {
            case "android":
                app = (String) config.get("androidApp");
                device = (String) config.get("androidDevice");
                os = (String) config.get("androidVersion");
                break;
            case "ios":
                device = (String) config.get("iOSDevice");
                os = (String) config.get("iOSVersion");
                app = (String) config.get("iosApp");
                break;
            default:
                throw new IllegalArgumentException("Unsupported OS: " + platform);
        }

        capabilities.setCapability("device", device);
        capabilities.setCapability("os_version", os);
        capabilities.setCapability("platformName", platform);
        capabilities.setCapability("app", app);
        capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 60);
        capabilities.setCapability("idleTimeout", "1000");
        capabilities.setCapability("settings[enableMultiWindows]", true);

        String url = reader.urlBrowserStack(testMethod);

        if ("android".equals(platform)) {
            appiumDriver = new AppiumDriver<>(new URL(url), capabilities);
        } else {
            appiumDriver = new IOSDriver<>(new URL(url), capabilities);
        }
    }

    private Map<String, Object> readConfigFromFile() throws IOException {
        Yaml yaml = new Yaml();
        try (FileReader fileReader = new FileReader("browserstack.yml")) {
            return yaml.load(fileReader);
        }
    }

    @AfterMethod(alwaysRun = true)
    public void tearDown(ITestResult testResult) throws Exception {
        ResultsListener result = new ResultsListener(appiumDriver);
        if (ITestResult.SUCCESS == testResult.getStatus()) {
            result.passResult(appiumDriver);
            Thread.sleep(3000);
        } else {
            result.failResult(appiumDriver);
            Thread.sleep(3000);
        }
        appiumDriver.quit();
    }
}

BasePage基类

public class BasePage extends BrowserStackConfig{

    protected AppiumDriver<MobileElement> appiumDriver; 

    public BasePage (AppiumDriver<MobileElement> appiumDriver) {
        appiumDriver = this.appiumDriver;
    }
    
}

LandingPage页面对象

public class LandingPage extends BasePage {

    public LandingPage(AppiumDriver<MobileElement> appiumDriver) {
        super(appiumDriver);
    }

    // Element locators
    private AndroidElement  logInBtn = (AndroidElement ) new WebDriverWait(appiumDriver, 10).until(
        ExpectedConditions.visibilityOfElementLocated(MobileBy.id(
            "button_log_in"
        ))
    );

    // Methods
    public boolean isInitialized() {
        try {
            return logInBtn.isDisplayed();
        } catch (Exception e) {
            return false;
        }
    }
}

AppTest测试类

public class AppTest extends BrowserStackConfig {

    private final SoftAssert assertion = new SoftAssert();

    @Test
    public void test() throws Exception {
        LandingPage landingPage = new LandingPage(appiumDriver);

        // assert landing page is loaded
        assertion.assertTrue(landingPage.isInitialized());
    }
}

问题根源

  1. BasePage构造方法赋值颠倒:appiumDriver = this.appiumDriver;将自身未初始化的null值覆盖传入的有效Driver实例,导致BasePage的成员变量appiumDriver始终为null。
  2. 元素初始化时机错误:LandingPage的logInBtn在类实例化时直接初始化,此时appiumDriver还未被正确赋值,触发空指针。
  3. 冗余继承:BasePage继承BrowserStackConfig,测试类也继承该类,导致Driver实例管理混乱,增加空指针风险。

修复方案

1. 修正BasePage构造逻辑

移除不必要的继承,将传入的Driver正确赋值给成员变量:

public class BasePage {
    protected AppiumDriver<MobileElement> appiumDriver; 

    public BasePage(AppiumDriver<MobileElement> appiumDriver) {
        this.appiumDriver = appiumDriver;
    }
}

2. 延迟元素初始化(推荐使用PageFactory)

使用Appium的PageFactory来管理元素,避免在类加载时直接初始化:

public class LandingPage extends BasePage {

    public LandingPage(AppiumDriver<MobileElement> appiumDriver) {
        super(appiumDriver);
        // 初始化PageFactory元素
        PageFactory.initElements(new AppiumFieldDecorator(appiumDriver), this);
    }

    @FindBy(id = "button_log_in")
    private AndroidElement logInBtn;

    public boolean isInitialized() {
        try {
            return new WebDriverWait(appiumDriver, 10)
                    .until(ExpectedConditions.visibilityOf(logInBtn))
                    .isDisplayed();
        } catch (Exception e) {
            return false;
        }
    }
}

3. 验证Driver初始化有效性

在测试方法中添加日志或打印,确认@BeforeMethod是否成功初始化appiumDriver:

@Test
public void test() throws Exception {
    System.out.println("Driver instance: " + appiumDriver); // 验证Driver是否为null
    LandingPage landingPage = new LandingPage(appiumDriver);
    assertion.assertTrue(landingPage.isInitialized());
    assertion.assertAll();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 23:49:53