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

Appium+Java+TestNG启动前出现NullPointerException求助

解决Appium+Cucumber+TestNG启动前的NullPointerException问题

看起来你遇到的核心问题是Cucumber和TestNG的生命周期不兼容,导致Hooks里的初始化逻辑完全没执行,进而引发driver空指针,甚至Appium服务都没机会启动。咱们一步步拆解解决:

问题根源分析

你的报错指向screens.myAccountOverviewScreen.logoutAccount里的driver.manage().timeouts(),但你说Appium和模拟器都没启动——这说明你的Hooks.startAppiumServer方法根本没被执行!

原因是:你用了AbstractTestNGCucumberTests集成Cucumber和TestNG,但Cucumber有自己的钩子体系(@Before/@After),TestNG的@BeforeTest注解并不会被Cucumber的测试流程触发。所以你的driver和AppiumService都没初始化,第一次调用driver自然就抛出NullPointerException。

另外,你的TestRunner的CucumberOptions里没有指定glue路径,Cucumber可能找不到你的Hooks类,这也会导致钩子不执行。

具体解决步骤

1. 替换TestNG钩子为Cucumber钩子

把Hooks类里的@BeforeTest和@AfterTest换成Cucumber的@Before和@After注解(注意导入cucumber.api.java.Before/After,而非TestNG的同名注解):

package MA.test;

import cucumber.api.java.After;
import cucumber.api.java.Before;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.remote.AndroidMobileCapabilityType;
import io.appium.java_client.remote.MobileCapabilityType;
import io.appium.java_client.service.local.AppiumDriverLocalService;
import io.appium.java_client.service.local.AppiumServiceBuilder;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.MalformedURLException;
import java.net.URL;

public class Hooks {
    public AppiumDriver<MobileElement> driver;
    public AppiumDriverLocalService service;

    @Before
    public void startAppiumServer() throws InterruptedException, MalformedURLException {
        // 这里先暂时用固定值测试,后续可结合TestNG参数优化传递逻辑
        String platformVersion = "9.0";
        String emulatorNumber = "emulator-5554";
        String deviceName = "Android9_Nexus";
        String port = "4723";
        
        System.out.println("\n ABC");
        service = new AppiumServiceBuilder()
                .usingPort(Integer.valueOf(port))
                .build();
        service.start();

        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability(MobileCapabilityType.PLATFORM_VERSION, platformVersion);
        caps.setCapability(MobileCapabilityType.DEVICE_NAME, emulatorNumber);
        caps.setCapability(AndroidMobileCapabilityType.AVD, deviceName);
        caps.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
        caps.setCapability(MobileCapabilityType.APPLICATION_NAME, "Name");
        caps.setCapability(MobileCapabilityType.APPIUM_VERSION, "1.14.0");
        caps.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, "activity");
        caps.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "package");
        caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");

        driver = new AndroidDriver<MobileElement>(new URL("http://0.0.0.0:" + port + "/wd/hub"), caps);
        System.out.println("\n Appium server: " + service.getUrl());
        Thread.sleep(2000);
    }

    @After
    public void teardown() {
        // 先判断对象不为空再执行,避免二次空指针
        if (driver != null) {
            driver.closeApp();
            driver.quit();
        }
        if (service != null && service.isRunning()) {
            service.stop();
        }
        System.out.println("\n Test quit");
    }

    // 提供获取driver的方法,供screen类调用
    public AppiumDriver<MobileElement> getDriver() {
        return driver;
    }
}

2. 配置TestRunner的glue路径

在TestRunner的CucumberOptions里添加glue,指定Hooks和步骤定义所在的包,让Cucumber能扫描到它们:

package MA.steps;

import cucumber.api.CucumberOptions;
import cucumber.api.testng.AbstractTestNGCucumberTests;

@CucumberOptions(
        plugin = {"pretty", "html:target/cucumber-reports"},
        monochrome = true,
        features = "src/test/java/feature",
        tags = "@Login",
        glue = {"MA.test", "MA.steps"} // 添加这一行,指定钩子和步骤类的包
)
public class TestRunner extends AbstractTestNGCucumberTests {
}

3. 让Screen类获取正确的Driver实例

你的myAccountOverviewScreen类需要拿到Hooks里初始化的driver实例,不要使用未初始化的变量。可以通过构造方法注入:

package screens;

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import java.util.concurrent.TimeUnit;

public class myAccountOverviewScreen {
    private AppiumDriver<MobileElement> driver;

    // 构造方法接收driver
    public myAccountOverviewScreen(AppiumDriver<MobileElement> driver) {
        this.driver = driver;
    }

    public void logoutAccount() {
        // 现在driver已经初始化,不会抛出空指针
        driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
        // 后续业务逻辑...
    }
}

然后在你的Cucumber步骤定义类里,通过Hooks获取driver并传入screen类:

package MA.steps;

import MA.test.Hooks;
import cucumber.api.java.en.Given;
import screens.myAccountOverviewScreen;

public class LoginSteps {
    private myAccountOverviewScreen accountScreen;

    public LoginSteps(Hooks hooks) {
        this.accountScreen = new myAccountOverviewScreen(hooks.getDriver());
    }

    @Given("^用户执行登出操作$")
    public void user_logout() {
        accountScreen.logoutAccount();
    }
}

4. 优化依赖版本(可选但推荐)

你的TestNG用的是beta版本7.0.0-beta3,建议换成稳定版避免兼容性问题,比如:

<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.4.0</version>
</dependency>

验证效果

修改后运行测试,你应该能看到控制台输出ABC和Appium server的URL,说明Hooks的初始化逻辑已经执行,Appium服务和模拟器会正常启动,screen类里的driver也不会再抛出NullPointerException了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:26:04