使用@BeforeClass初始化Appium AndroidDriver时遇NullPointerException求助
问题:Appium+TestNG初始化驱动后页面对象报NullPointerException
在使用TestNG+Appium做自动化测试时,出现以下问题:
- 通过
@BeforeSuite启动Appium服务 - Base类中使用
@BeforeClass注解的静态方法setDriver初始化AndroidDriver,通过getDriver()方法获取驱动 - 测试类继承Base后,在成员变量处调用
getDriver()初始化页面对象 - 运行测试方法时抛出
NullPointerException,提示Cannot invoke "org.openqa.selenium.SearchContext.findElement(org.openqa.selenium.By)" because "this.searchContext" is null
Base类代码
package setup; import java.net.URL; import java.time.Duration; import org.apache.commons.lang3.RandomStringUtils; import org.openqa.selenium.remote.DesiredCapabilities; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; import java.io.IOException; import java.net.MalformedURLException; import io.appium.java_client.android.AndroidDriver; import io.github.bonigarcia.wdm.WebDriverManager; import net.bytebuddy.utility.RandomString; public class Base { String deviceIp = "192.148.31.156"; Runtime runtime = Runtime.getRuntime(); public static AndroidDriver driverA; static URL appiumURL; static DesiredCapabilities capabilities = new DesiredCapabilities(); @BeforeSuite public void startServer() { try { runtime.exec("cmd.exe /c start cmd.exe /k adb connect " + deviceIp + ""); runtime.exec("cmd.exe /c start cmd.exe /k appium -a 127.0.0.1 -p 4723"); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } @BeforeClass public static AndroidDriver setDriver() { capabilities.setCapability("automationName", "Appium"); capabilities.setCapability("platformName", "Android"); capabilities.setCapability("platformVersion", "12.0"); capabilities.setCapability("deviceName", "192"); capabilities.setCapability("unlockKey", "934448"); capabilities.setCapability("app", "C:\\Users\\DELL\\Desktop\\APK files\\YEO App Android\\app.apk"); capabilities.setCapability("autoGrantPermissions", "true"); try { appiumURL = new URL("http://127.0.0.1:4733/wd/hub"); driverA = new AndroidDriver(appiumURL, capabilities); // driverA.manage().timeouts().implicitlyWait(10, nulls); driverA.manage().timeouts().implicitlyWait(Duration.ofSeconds(30)); } catch (MalformedURLException e) { e.printStackTrace(); } return driverA; } @AfterSuite public void stopServer() { try { runtime.exec("taskkill /F /IM node.exe"); runtime.exec("taskkill /F /IM cmd.exe"); } catch (Exception e) { e.printStackTrace(); } } public static AndroidDriver getDriver() { // this.setDriver(); return driverA; } }
测试类代码
package testcases; import org.testng.annotations.Test; import Resources.RuntimeHandler; import org.testng.annotations.Test; import io.github.bonigarcia.wdm.WebDriverManager; import pageFactories.LaunchScreen; import pageFactories.MessagesScreen; import pageFactories.SettingsScreen; import pageFactories.YEOLockScreen; import setup.Base; import org.openqa.selenium.WebDriver; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeSuite; import org.testng.annotations.BeforeTest; import io.appium.java_client.android.AndroidDriver; public class TC_LoginToYEO extends Base { LaunchScreen launch = new LaunchScreen(getDriver()); YEOLockScreen lock = new YEOLockScreen(getDriver()); SettingsScreen settings = new SettingsScreen(getDriver()); MessagesScreen ms = new MessagesScreen(getDriver()); @Test(priority = 0) public void login_To_YEO() { launch.select_Country_Code("India"); launch.login("891", "3"); } }
报错信息
java.lang.NullPointerException: Cannot invoke "org.openqa.selenium.SearchContext.findElement(org.openqa.selenium.By)" because "this.searchContext" is null at org.openqa.selenium.support.pagefactory.DefaultElementLocator.findElement(DefaultElementLocator.java:70) at org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler.invoke(LocatingElementHandler.java:39) at jdk.proxy2/jdk.proxy2.$Proxy12.click(Unknown Source) at pageFactories.LaunchScreen.nav_To_LoginPage(LaunchScreen.java:62) at pageFactories.LaunchScreen.select_Country_Code(LaunchScreen.java:67) at testcases.TC_LoginToYEO.login_To_YEO(TC_LoginToYEO.java:32) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:139) at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:677) at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:221) at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50) at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:962) at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:194) at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:148) at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.testng.TestRunner.privateRun(TestRunner.java:806) at org.testng.TestRunner.run(TestRunner.java:601) at org.testng.SuiteRunner.runTest(SuiteRunner.java:433) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:427) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:387) at org.testng.SuiteRunner.run(SuiteRunner.java:330) at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52) at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95) at org.testng.TestNG.runSuitesSequentially(TestNG.java:1256) at org.testng.TestNG.runSuitesLocally(TestNG.java:1176) at org.testng.TestNG.runSuites(TestNG.java:1099) at org.testng.TestNG.run(TestNG.java:1067) at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:115) at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:251) at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:77)
解决方案
1. 修复TestNG注解方法的静态修饰符问题
TestNG不会正确执行带有static修饰的@BeforeClass方法,导致driverA始终未初始化。修改Base类的setDriver方法,去掉static:
@BeforeClass public AndroidDriver setDriver() { // 移除static修饰符 capabilities.setCapability("automationName", "Appium"); capabilities.setCapability("platformName", "Android"); capabilities.setCapability("platformVersion", "12.0"); capabilities.setCapability("deviceName", "192"); capabilities.setCapability("unlockKey", "934448"); capabilities.setCapability("app", "C:\\Users\\DELL\\Desktop\\APK files\\YEO App Android\\app.apk"); capabilities.setCapability("autoGrantPermissions", "true"); try { appiumURL = new URL("http://127.0.0.1:4723/wd/hub"); // 同时修正端口 driverA = new AndroidDriver(appiumURL, capabilities); driverA.manage().timeouts().implicitlyWait(Duration.ofSeconds(30)); } catch (MalformedURLException e) { e.printStackTrace(); } return driverA; }
同时更新getDriver方法,去掉static:
public AndroidDriver getDriver() { return driverA; }
2. 延迟页面对象初始化时机
测试类中成员变量的初始化时机早于@BeforeClass方法执行,此时getDriver()返回null,导致页面对象的searchContext为空。需将页面对象初始化放在@BeforeClass方法中:
public class TC_LoginToYEO extends Base { LaunchScreen launch; YEOLockScreen lock; SettingsScreen settings; MessagesScreen ms; @BeforeClass public void initPageObjects() { launch = new LaunchScreen(getDriver()); lock = new YEOLockScreen(getDriver()); settings = new SettingsScreen(getDriver()); ms = new MessagesScreen(getDriver()); } @Test(priority = 0) public void login_To_YEO() { launch.select_Country_Code("India"); launch.login("891", "3"); } }
3. 修正Appium服务端口不一致问题
Base类中启动Appium服务用的是端口4723,但初始化驱动时用的是4733,端口不匹配会导致驱动初始化失败,需将URL中的端口改为4723:
appiumURL = new URL("http://127.0.0.1:4723/wd/hub");
4. 优化Appium服务启动的可靠性
直接用Runtime.exec启动服务可能存在服务未完全就绪就初始化驱动的问题,建议使用AppiumServiceBuilder来管理服务:
import io.appium.java_client.service.local.AppiumDriverLocalService; import io.appium.java_client.service.local.AppiumServiceBuilder; @BeforeSuite public void startServer() { // 构建并启动Appium服务 AppiumDriverLocalService appiumService = new AppiumServiceBuilder() .withIPAddress("127.0.0.1") .usingPort(4723) .build(); appiumService.start(); // 连接设备 try { runtime.exec("adb connect " + deviceIp); Thread.sleep(2000); // 等待设备连接完成 } catch (IOException | InterruptedException e) { e.printStackTrace(); } } @AfterSuite public void stopServer() { // 停止Appium服务 AppiumDriverLocalService appiumService = AppiumDriverLocalService.getService(); if (appiumService != null && appiumService.isRunning()) { appiumService.stop(); } }
内容的提问来源于stack exchange,提问作者shiva durga
相关产品推荐
相关产品推荐

