Java16环境下Appium+Serenity框架PageFactory空指针异常求助
问题描述
运行基于Serenity BDD的Appium测试框架时抛出异常:
net.serenitybdd.core.exceptions.StepInitialisationException: Failed to create step library for TestAppPage:Cannot invoke "Object.getClass()" because "object" is null
异常触发场景:使用PageFactory初始化的元素执行element.sendKeys(text)操作时抛出;改用driver.findElement(element).sendKeys(userName)方式操作元素则正常运行。
项目相关代码
TestAppPage.java
package pages; import io.appium.java_client.pagefactory.AndroidFindBy; import io.appium.java_client.pagefactory.iOSXCUITFindBy; import net.thucydides.core.annotations.Step; import org.openqa.selenium.WebElement; public class TestAppPage extends AppiumBaseScreen { @AndroidFindBy(id ="IntegerA") @iOSXCUITFindBy(accessibility = "IntegerA") private WebElement txtFirstNumberField; @AndroidFindBy(id ="IntegerB") @iOSXCUITFindBy(accessibility = "IntegerB") private WebElement txtSecondNumberField; @AndroidFindBy(id ="ComputeSumButton") @iOSXCUITFindBy(accessibility = "ComputeSumButton") private WebElement btnComputeSum; @AndroidFindBy(id ="Answer") @iOSXCUITFindBy(accessibility = "Answer") private WebElement lblSumResults; @Step("Enter 1st number") public void enterFirstNumber(int number) { setText(String.valueOf(number), txtFirstNumberField); } @Step("Enter 2nd number") public void enterSecondNumber(int number) { setText(String.valueOf(number), txtSecondNumberField); } @Step("Press sum button") public void pressSumButton() { btnComputeSum.click(); } @Step("Return sum of numbers") public String getSumOfNumbers() { return lblSumResults.getText(); } }
AppiumBaseScreen.java
package pages; import io.appium.java_client.pagefactory.AppiumFieldDecorator; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.support.PageFactory; import static driver.DriverBase.getAppiumDriver; public class AppiumBaseScreen { public AppiumBaseScreen() { PageFactory.initElements(new AppiumFieldDecorator(getAppiumDriver()), this); } protected void setText(String text, WebElement element) { click(element); element.clear(); element.sendKeys(text); } protected void click(WebElement element) { moveFocusOnElement(element); element.click(); } protected void moveFocusOnElement(WebElement element) { new Actions(getAppiumDriver()).moveToElement(element).perform(); } }
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.appiumpractice</groupId> <artifactId>AppiumPractice</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>16</maven.compiler.source> <maven.compiler.target>16</maven.compiler.target> <serenity.version>3.2.5</serenity.version> <serenity.cucumber.version>3.2.5</serenity.cucumber.version> <cucumber.version>7.3.3</cucumber.version> </properties> <build> <plugins> <plugin> <groupId>net.serenity-bdd.maven.plugins</groupId> <artifactId>serenity-maven-plugin</artifactId> <version>${serenity.version}</version> <executions> <execution> <id>serenity-reports</id> <phase>post-integration-test</phase> <goals> <goal>aggregate</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>net.serenity-bdd</groupId> <artifactId>serenity-cucumber</artifactId> <version>${serenity.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency> <dependency> <groupId>org.jeasy</groupId> <artifactId>easy-random-core</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.17.2</version> </dependency> <dependency> <groupId>com.googlecode.json-simple</groupId> <artifactId>json-simple</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>com.github.javafaker</groupId> <artifactId>javafaker</artifactId> <version>1.0.2</version> </dependency> </dependencies> </project>
DriverBase.java
package driver; import io.appium.java_client.AppiumDriver; import io.appium.java_client.ios.IOSDriver; import io.appium.java_client.ios.options.XCUITestOptions; import io.appium.java_client.safari.options.SafariOptions; import java.net.URL; public class DriverBase { private static AppiumDriver driver; public static void initializeDriver(URL appiumURL, XCUITestOptions capabilities) { initIOSDriver(appiumURL, capabilities); } public static void initializeDriver(URL appiumURL, SafariOptions capabilities) { initIOSDriver(appiumURL, capabilities); } public static AppiumDriver getAppiumDriver() { return driver; } private static void initIOSDriver(URL appiumURL, XCUITestOptions capabilities) { driver = new IOSDriver(appiumURL, capabilities); } private static void initIOSDriver(URL appiumURL, SafariOptions capabilities) { driver = new IOSDriver(appiumURL, capabilities); } public static void closeDriver() { driver.close(); driver.quit(); } }
问题分析与解决方案
核心原因
- Driver初始化时机错误:
AppiumBaseScreen构造函数执行时,DriverBase.getAppiumDriver()返回null,导致AppiumFieldDecorator无法正常初始化元素,后续调用元素方法触发空指针相关异常。 - Serenity BDD机制冲突:Serenity会自动管理Page对象的生命周期,直接使用原生Selenium的
PageFactory会破坏其内部逻辑,导致元素初始化失败。
修复步骤
1. 改用Serenity原生页面对象基类
移除手动PageFactory初始化,继承Serenity的PageObject类,它会自动处理Appium元素的初始化:
package pages; import net.serenitybdd.core.pages.PageObject; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; import static driver.DriverBase.getAppiumDriver; public class AppiumBaseScreen extends PageObject { protected void setText(String text, WebElement element) { click(element); element.clear(); element.sendKeys(text); } protected void click(WebElement element) { moveFocusOnElement(element); element.click(); } protected void moveFocusOnElement(WebElement element) { new Actions(getAppiumDriver()).moveToElement(element).perform(); } }
2. 调整Driver初始化顺序
在测试类的前置钩子中先初始化Driver,再创建Page对象,确保Driver已就绪:
import driver.DriverBase; import io.appium.java_client.ios.options.XCUITestOptions; import org.junit.Before; import java.net.URL; public class SumTest { private TestAppPage testAppPage; @Before public void setup() throws Exception { // 先初始化Driver XCUITestOptions options = new XCUITestOptions() .setDeviceName("iPhone 14") .setApp("/path/to/your/test.app"); DriverBase.initializeDriver(new URL("http://127.0.0.1:4723"), options); // 再初始化Page对象并绑定Driver testAppPage = new TestAppPage(); testAppPage.setDriver(DriverBase.getAppiumDriver()); } }
3. 添加Serenity-Appium集成依赖
在pom.xml中补充缺失的Appium集成依赖,确保Serenity能正确处理Appium元素:
<dependency> <groupId>net.serenity-bdd</groupId> <artifactId>serenity-appium</artifactId> <version>${serenity.version}</version> </dependency> <dependency> <groupId>io.appium</groupId> <artifactId>java-client</artifactId> <version>8.3.0</version> </dependency>
验证修复
完成上述修改后,重新运行测试,PageFactory初始化的元素可正常执行sendKeys等操作,不会再抛出StepInitialisationException异常。
内容的提问来源于stack exchange,提问作者Bilal
相关产品推荐
相关产品推荐

