Features

Appium and Page Object Model

  • Share on Facebook
  • Share on Twitter
  • Share on LinkedIn
  • Share on HackerNews

The Page Object Model, also known as POM, is a design pattern in Selenium and Appium that creates an object repository for storing all web elements. It provides cleaner test code, separation of concerns and reduces overall code duplication.
The Page Object Model principle keeps locators and test login separately from each other.
A single Page Object is an instantiation of an object-orientated class, describing locators on a specific page (or view) and its interactions.

What are the advantages of using Page Object Model?

There are several advantages to using the Page Object Model design pattern.

  • Improves code maintenance: All locators for a specific flow are grouped together in a specific POM. If a page or application changes its UI, you no longer need to modify all your tests scattered with locators. Simply change it in the page object model.
  • Encourages code reuse: There is no need to write the same logic across different tests. For example, no need to keep doing the same log in flow if your test requires a logged-in state. Keep all logic for a specific page or action in its own POM file.
  • Improves code readability: You can find where specific test code is located more easily. If a specific change is required, you can do it in the appropriate file. You no longer need to modify several files for a change in one specific test logic.

POM Example

A good example of a Page Object Model would be the Login Page Object.
This will be responsible for typing in the username and password, and proceeding with the actual login.
Other test cases can use this POM when a logged-in state is required.


package com.testingbot.example;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import io.appium.java_client.android.AndroidKeyCode;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import org.openqa.selenium.support.PageFactory;
public class LoginPage {
    private AndroidDriver<AndroidElement> driver;
    public LoginPage() {
    }
    public LoginPage(AndroidDriver<AndroidElemen>> driver) {
        this.driver = driver;
    }
    By keyboard = By.className("UIAKeyboard");
    By userNameElement = By.id("userName");
    By passwordElement = By.id("password");
    By loginElement = By.id("login");

    public boolean isDisplayed() {
        return loginElement.isDisplayed();
    }
    public void typeUserName(String name) {
        userNameElement.sendKeys(name);
    }
    public void typePassword(String password) {
        passwordElement.sendKeys(password);
    }
    public void clickLogin() {
        loginElement.click();
    }
    public void hideKeyboardIfVisible() {
        if (keyboard != null) {
            driver.pressKeyCode(AndroidKeyCode.KEYCODE_ESCAPE);
        }
    }
    public void login (String name, String password) {
        hideKeyboardIfVisible();
        typeUserName(name);
        typePassword(password);
        clickLogin();
    }
}

This is an example of a Login Page Object Model. The login method will be responsible for several things. It will hide the soft keyboard, type in a username, a password and submit.


This is logic that may be used by several other test cases that require a logged in state.

Of course, you can implement Page Object Model in any programming language; Java, Kotlin, Swift, ...

How can I use the Page Factory with Appium?

You can use Selenium's PageFactory class with Appium and POM.
The PageFactory class can be used with initElements to initialize all elements located by the @FindBy (or AndroidFindBy) annotation.
PageFactory will automatically assign the elements to your Page Object Model.

package com.testingbot.example;
import io.appium.java_client.MobileBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import io.appium.java_client.android.AndroidKeyCode;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import org.openqa.selenium.support.PageFactory;
import java.util.concurrent.TimeUnit;

public class ExamplePage {
    private AndroidDriver driver;
    public ProfilePage() {
    }
    public ProfilePage(AndroidDriver driver) {
        this.driver = driver;
        PageFactory.initElements(new AppiumFieldDecorator(driver), this);
    }
    @AndroidFindBy(className = "UIAKeyboard")
    private AndroidElement keyboard;
    @AndroidFindBy(id = "userName")
    private AndroidElement userNameElement;
    @AndroidFindBy(id = "password")
    private AndroidElement passwordElement;
    @AndroidFindBy(id = "login")
    private AndroidElement loginElement;
    public boolean isDisplayed() {
        return loginElement.isDisplayed();
    }
    public void typeUserName(String name) {
        userNameElement.sendKeys(name);
    }
    public void typePassword(String password) {
        passwordElement.sendKeys(password);
    }
    public void clickLogin() {
        loginElement.click();
    }
    public void hideKeyboardIfVisible() {
        if (keyboard != null) {
            driver.pressKeyCode(AndroidKeyCode.KEYCODE_ESCAPE);
        }
    }
    public void login (String name, String password) {
        hideKeyboardIfVisible();
        typeUserName(name);
        typePassword(password);
        clickLogin();
    }
}

There are various annotations which you can use for declaring elements:

  • @FindBy

    PageFactory will automatically assign elements annotated with @FindBy, skipping the requirement of using driver.findElement()

  • @FindBys

    Use more than 1 criteria to locate elements, using an AND condition; all criteria must be met. It uses nested @FindBy annotations to locate an element.

    @FindBys({
     @FindBy(id = "form"),
     @FindBy(name = "username")
    })
    
    FormUserNameButton;
  • @FindAll

    Similar to @FindBys but uses an OR condition; at least one criteria must be matched.

    @FindAll({
     @FindBy(id = "form"), // does not match
    
     @FindBy(name = "username")
    })
    
    FormUserNameButton;
  • @CacheLookUp

    Can be used to keep specific elements in memory. If your tests use a specific element frequently, you might see a small speedup using this.

    @CacheLookUp
    @FindAll({
     @FindBy(id = "form"),
     @FindBy(name = "username")
    })
    
    FormUserNameButton;

What are the different locator strategies that are used for @FindBy?

FindBy uses the same locator strategies as the findElement() method used in POM.

  • id
  • name
  • className
  • xpath
  • css
  • tagName
  • linkText
  • partialLinkText

When should I use the CacheLookUp annotation with PageFactory?

Using the @CacheLookUp annotation has both its advantages and disadvantages.


If you are looking up an element frequently during your tests, and the elements are static without ever changing, then it might be beneficial to use CacheLookup. It will improve the overall test execution.


The disadvantage of using this annotation is when the element does change, you may end up with a StaleElementReferenceException. If the element is dynamic and bound to change during the test, do not use the CacheLookUp annotation.

  • Share on Facebook
  • Share on Twitter
  • Share on LinkedIn
  • Share on HackerNews
TestingBot Logo

Sign up for a Free Trial

Start testing your apps with TestingBot.

No credit card required.

Other Articles

Getting Started with Appium

Learn how to use Appium for automated testing. We will provide some tips and tricks, performance optimizations and ways to use Appium Inspector to troubleshoot your native mobile app testing.

Read more
Getting Started with IntelliJ and Selenium WebDriver

A brief overview on how to setup, configure and run Selenium WebDriver tests with IntelliJ IDEA.

Read more
Internationalization testing of websites and mobile apps

Learn more about Internationalization Testing: how to prepare and test your website or mobile app for different regions in the world.

Read more