How do I create Page Object Model that works for Android AND iOS

I have created a Page Object in Java that is currently working for an Android app as shown below:

public class MattVerifyPage extends PageObject{

private AppiumDriver driver = FrameworkInitialize.driver;

By verifyTitle = By.xpath("/hierarchy/android.widget.TextView");

public void verifyTitle(String expectedTitle){

    String actualTitle = driver.findElement(verifyTitle).getText();

However, I need it to work an the Android app and the iOS app, the xpath selector is different for both apps. I think I need to do something like this:

@AndroidFindBy(xpath = “androidxpath”)
@iOSFindBy(xpath = “iOSxpath”)
public MobileElement verifyTitle ;

However, when I do this, the driver.findElement line (String actualTitle = driver.findElement(verifyTitle).getText() shows the following error:

findElement
(org.openqa.selenium.By)
in DefaultGenericMobileDriver cannot be applied
to
(io.appium.java_client.MobileElement)

I think I am comparing AppiumElements with SeleniumElements but I’m not sure how to resolve it.

Any help would be greatly appreciated.

Thanks

Matt

@Matt882 when you use Appium annotation they already do all job to find element. All left is use it.

@AndroidFindBy(xpath = “androidxpath”)
@iOSFindBy(xpath = “iOSxpath”)
public MobileElement verifyTitle ;

String actualTitle = verifyTitle.getText();

first example from google search to see all PageObject - > https://blog.testproject.io/2018/07/31/page-object-model-appium-java-android/

or some example at java-client Appium code: https://github.com/appium/java-client/blob/master/src/test/java/io/appium/java_client/pagefactory_tests/MobileBrowserCompatibilityTest.java

1 Like

Thanks Aleksei. I have changed my page object to look like this:

public class MattVerifyPage extends PageObject{
private AppiumDriver driver;

public MattVerifyPage(AppiumDriver driver) {
    this.driver = driver;
    PageFactory.initElements(new AppiumFieldDecorator(driver), this);
}


@AndroidFindBy(xpath = "Androidxpath")
@iOSFindBy(xpath = "iOSxpath")
public MobileElement verifyTitle ;


public void verifyTitle(String expectedTitle){

    String actualTitle =verifyTitle.getText();

}

In my test step file the page is instantiated using this code:

MattVerifyPage VerifyPage = new MattVerifyPage(driver);

However, when I run the test I get the error shown below. I am also using Serenity so if there is no obvious problem with my code above it must be a Serenity issue:

java.lang.IllegalArgumentException: Can not set io.appium.java_client.MobileElement field com.xxx.test.pages.MattVerifyPage.verifyTitle to com.sun.proxy.$Proxy19
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
at java.lang.reflect.Field.set(Field.java:764)
at org.openqa.selenium.support.PageFactory.proxyFields(PageFactory.java:117)
at org.openqa.selenium.support.PageFactory.initElements(PageFactory.java:105)
at net.thucydides.core.annotations.locators.SmartElementProxyCreator.lambda$new$3(SmartElementProxyCreator.java:37)
at net.thucydides.core.annotations.locators.SmartElementProxyCreator.proxyElements(SmartElementProxyCreator.java:48)
at net.thucydides.core.webdriver.DefaultPageObjectInitialiser.apply(DefaultPageObjectInitialiser.java:21)
at net.serenitybdd.core.pages.PageObject.setDriver(PageObject.java:151)
at net.serenitybdd.core.pages.PageObject.setDriver(PageObject.java:155)
at net.thucydides.core.steps.PageObjectDependencyInjector.updatePageObject(PageObjectDependencyInjector.java:69)
at net.thucydides.core.steps.PageObjectDependencyInjector.instantiatePageObjectIfNotAssigned(PageObjectDependencyInjector.java:59)
at net.thucydides.core.steps.PageObjectDependencyInjector.injectDependenciesInto(PageObjectDependencyInjector.java:33)
at net.serenitybdd.core.Serenity.injectDependenciesInto(Serenity.java:61)
at net.serenitybdd.core.Serenity.initializeWithNoStepListener(Serenity.java:102)
at cucumber.runtime.SerenityObjectFactory.newInstance(SerenityObjectFactory.java:68)
at cucumber.runtime.SerenityObjectFactory.cacheNewInstance(SerenityObjectFactory.java:51)
at cucumber.runtime.SerenityObjectFactory.getInstance(SerenityObjectFactory.java:41)
at cucumber.runtime.java.JavaStepDefinition.execute(JavaStepDefinition.java:37)
at cucumber.runtime.StepDefinitionMatch.runStep(StepDefinitionMatch.java:40)
at cucumber.api.TestStep.executeStep(TestStep.java:102)
at cucumber.api.TestStep.run(TestStep.java:83)
at cucumber.api.TestCase.run(TestCase.java:58)
at cucumber.runner.Runner.runPickle(Runner.java:80)
at cucumber.runtime.Runtime.runFeature(Runtime.java:119)
at cucumber.runtime.Runtime.run(Runtime.java:104)
at cucumber.api.cli.Main.run(Main.java:36)
at cucumber.api.cli.Main.main(Main.java:18)
at ✽.I see the title is “MattVerify”(C:/dev/GBG_Automation/src/test/resources/features/01_GBG Verify/01_01_SamsungS9_Android_OS8.feature:6)

It is problem in your serenity/cucumber. Your example works correctly in pure Java.

One suggestion is move constructor to basePage.

Hi @Aleksei

I would like to ask if PageObject model in Android can work in Appium Webview or hybrid app? Currently using CSS selector when using a selector. Does it also works or can be applied to Appium Webview? If it does, do I also use this @AndroidFindBy annotations in webview/hybrid app? What is the equivalent of @AndroidFindBy annotations when using css selector page object model? Thank you @Aleksei

Regards,
Chris

@p0well493 Hi.
Appium decorator work everywhere: android, ios, hybid app webView or even pure web (no mobile tests at all).

NB!!! Before use it you need switch to WebView with hybridApp!

to use CSS:

import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import java.time.Duration;

public class MyPagePage {

    private static int DEFAULT_WAIT_TIME = 20;
    private WebDriver driver;

    @FindBy(css = "button[class^='CountrySelector-']")
    private WebElement countrySelectButton;

    public MyPagePage(WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(new AppiumFieldDecorator(driver, Duration.ofSeconds(DEFAULT_WAIT_TIME)), this);
    }

    public MyPagePage clickCountrySelectButton() {
        countrySelectButton.click();
        return this;
    }
}
1 Like

@Aleksei
its claimed that using pageFactory is not recommended to use according to Simon Stewart, was wondering what could be the alternative method specially when it comes to mobile automation

he did not say that we should not use it. he just said that we should use it a bit more wise. clear nice logic.

see his example (https://johnfergusonsmart.com/beyond-page-objects-liberate-chains-ui-think/):

@Test
public void should_be_able_to_complete_a_todo_with_steps() {
    todoListPage.openApplication();
    todoListPage.addATodoItemCalled("Put out the garbage");
    todoListPage.addATodoItemCalled("Walk the dog");
    todoListPage.markAsComplete("Walk the dog");
    assertThat(todoListPage.statusOf("Walk the dog"), is(Completed));
}

If you see at mine page object now test looks even better:

@Test
public void should_be_able_to_complete_a_todo_with_steps() {
    todoListPage
      .openApplication()
      .addATodoItemCalled("Put out the garbage")
      .addATodoItemCalled("Walk the dog")
      .markAsComplete("Walk the dog")
      .checkStatus(todoListPage.statusOf("Walk the dog"), is(Completed));
}