Better Page Objects strategy using AjaxElementLocatorFactory with Selenium and Java
Tags: java, testing, selenium, testautomation
Published: 2023-12-20 09:50:30

## Introduction

Page Objects Model is a Testing Pattern created by Selenium WebDriver to better model the web test architecture into the domain we are testing, creating classes that have all interactions with a web page.

There are thousands of articles on the internet, and I recommend you view the one from the Selenium WebDriver page: [https://www.selenium.dev/documentation/test_practices/encouraged/page_object_models](https://www.selenium.dev/documentation/test_practices/encouraged/page_object_models)

### Page Factory
**If you already know about it, please skip this section.**

Page Factory is a class that initializes all the elements in the page objects to reduce the class verbosity. Without the usage of the Page Factory class, we would have a class like this:

```java

public class PageObjectExample {

    private final WebDriver driver;

    public PageObjectExample(WebDriver driver) {
        this.driver = driver;
    }

    public void login(String email, String password) {
        driver.findElement(By.id("email")).sendKeys(email);
        driver.findElement(By.id("password")).sendKeys(password);
        driver.findElement(By.name("next")).click();
    }
}
```

Note that the method `login` has all the interactions with the page, where we are using the `driver` field to find and interact within the elements based on its locator.

This example is fine for a few elements on the page, but I’m sure you will have a lot of elements on some pages.

Now, what the Page Factory class proposes to do is mainly reduce the verbosity of the Page Object by using WebElements as fields in the class followed by the PageFactory class usage to instantiate the elements.

This will reduce the necessity to use `driver.findElement(By...)`

```java
public class PageObjectExample {
    
    @FindBy(id = "email")
    private WebElement email;

    @FindBy(id = "password")
    private WebElement password;

    @FindBy(name = "next")
    private WebElement next;

    public PageObjectExample(WebDriver driver) {
        PageFactory.initElements(driver, this);
    }

    public void login(String email, String password) {
        this.email.sendKeys(email);
        this.password.sendKeys(password);
        next.click();
    }
}
```

You can see on lines 3 to 10, that we are adding each element in the page as a `WebElement` field, using the `@FindBy` annotation to express its locator.

On line 13 you can see the usage of the `PageFactory` class using the `initiElements`, which will, behind the scenes, translate each element into `driver.findElement(By...)`.

On lines 17 to 19, you can see the usage of the element itself without the `driver.findElement(By…)` because it was already one by the `PageFactory` class. This reduces the amount of code and verbosity you would possibly have.

Please, also read this definition of Page Factory from the SeleniumHQ wiki: [https://github.com/SeleniumHQ/selenium/wiki/PageFactory](https://github.com/SeleniumHQ/selenium/wiki/PageFactory)

## The common problem during the Page Objects modeling

The main problem is the element **wait strategy**. For instance, we have three different ones in Selenium WebDriver and the recommended ones are the [Explicitly Wait](https://www.selenium.dev/documentation/webdriver/waits/#explicit-wait) and the [Fluent Wait](https://www.selenium.dev/documentation/webdriver/waits/#fluentwait). The [Implicitly Wait](https://www.selenium.dev/documentation/webdriver/waits/#implicit-wait) is, nowadays, considered a bad practice.


We have two main cases to apply a waiting strategy in the Page Objects: in the class constructor or the action methods.

Just a heads-up: we only use either Explicitly or Fluent Waits when necessary. Not for any action, we create!

### Wait for an element in the constructor

Normally this approach is used when you need to make sure the main element you will interact with is loaded into the page. You could replace this with the wait in the action method, of course, but this approach “tells” you explicitly that you are waiting for the element before proceeding with all the actions.

This is a simple example:

```java
public class PageObjectExample {

    private WebDriver driver;

    @FindBy(id = "email")
    private WebElement email;

    @FindBy(id = "password")
    private WebElement password;

    @FindBy(name = "next")
    private WebElement next;

    public PageObjectExample(WebDriver driver) {
        this.driver = driver;

        PageFactory.initElements(driver, this);
        new WebDriverWait(driver, Duration.of(5, ChronoUnit.SECONDS))
            .until(ExpectedConditions.visibilityOf(email));
    }

    public void login(String email, String password) {
        this.email.sendKeys(email);
        this.password.sendKeys(password);
        next.click();
    }
}
```

Note that, on lines 18 and 19, there’s an Explicitly Wait waiting until the email field is visible on the page.

### Wait for an element in the action methods

When an element that you are about to interact with is not present/visible/clickable we add an Explicitly Wait to make sure test integrity. We simply add an Explicitly Wait before the real action.

This is a simple example:

```java
public class PageObjectExample {

    private WebDriver driver;

    @FindBy(id = "email")
    private WebElement email;

    @FindBy(id = "password")
    private WebElement password;

    @FindBy(name = "next")
    private WebElement next;

    public PageObjectExample(WebDriver driver) {
        PageFactory.initElements(driver, this);
    }

    public void fillEmail(String email) {
        new WebDriverWait(driver, Duration.of(5, ChronoUnit.SECONDS))
            .until(ExpectedConditions.visibilityOf(this.email));
        
        this.email.sendKeys(email);
    }
}
```

You can see in lines 19 to 20, that we are waiting for the `email` field to be visible before filling in a value on it.

## How to reduce verbosity for the PageFactory and Waiting strategy

We can use two techniques:

1. Use the `AjaxElementLocatorFactory` in the P`ageFactory.initiElements` to apply an automatic explicit wait
2. Use the inheritance to avoid duplicated code for the `PageFactory.initiElements` usage

### What’s the AjaxElementLocatorFactory

This class in Selenium WebDriver adds a lazy load approach in the Page Factory class. It uses an Explicitly Wait when loading the element, which means when we use it, for at least de defined time you will express.

The usage of this class will reduce the necessity of explicitly waiting for elements in most cases. Of course, you might face cases where you need to add an Explicitly wait.

The regular code within the combination of the PageFactory class will look like this:

```java
public class PageObjectExample {

    private WebDriver driver;

    // WebElements ignored

    public PageObjectExample(WebDriver driver) {
        PageFactory.initElements(new AjaxElementLocatorFactory(driver, 5), this);
    }
}
```

You can see that we are instantiating the `AjaxElementLocatorFactory` inside of the `PageFactory.initElements`. This class needs the driver instance and the timeout in seconds.

This would be added in each Page Object class you have, but we have a better way to avoid it.

#### Using inheritance in our favor

Instead of adding this code snippet to each Page Object, we can create an abstract class to handle this job.

```java
public abstract class AbstractPageObject {

    private WebDriver driver;
    
    protected AbstractPageObject(WebDriver driver) {
        PageFactory.initElements(new AjaxElementLocatorFactory(driver, 5), this);
    }
}
```

Note we have an abstract class that will init the elements for the provided driver instance, adding the `AjaxElementLocatorFactory` and waiting until 5 seconds for the element, if necessary.

Now we need to extend it in the Page Object class, adopting the constructor and invoking `super` to use the one from the abstract class:

```java
public class PageObject extends AbstractPageObject {

    // WebElements ignored

    protected PageObject(WebDriver driver) {
        super(driver);
    }

   // action steps ignored
}
```

## Concrete example

In the [selenium-java-lean-test-architecture](https://github.com/eliasnogueira/selenium-java-lean-test-architecture) project you can see the following implemented structure:

* The class [AbstractPageObject](https://github.com/eliasnogueira/selenium-java-lean-test-architecture/blob/master/src/main/java/com/eliasnogueira/page/AbstractPageObject.java) has the Page Object initialization within the `AjaxElementLocatorFactory`
* The driver inside the `AbstractPageObject` is managed by the [DriverManager](https://github.com/eliasnogueira/selenium-java-lean-test-architecture/blob/master/src/main/java/com/eliasnogueira/driver/DriverManager.java) class which creates a thread instance to guarantee the parallel execution
* The `AbstractPageObject` class is being used in the [NavigationPage](https://github.com/eliasnogueira/selenium-java-lean-test-architecture/blob/master/src/main/java/com/eliasnogueira/page/booking/common/NavigationPage.java). Note that we don’t have the constructor using the super to send the driver instance as the `DriverManager` is taking care of the driver
* The [other page object](https://github.com/eliasnogueira/selenium-java-lean-test-architecture/tree/master/src/main/java/com/eliasnogueira/page/booking) classes are using the `NavigationPage`, so indirectly they are also using the `AbstractPageObject` class