Page Factory Design Pattern (Enhanced POM)

Selenium Page Factory Pattern is like an extension to Page Object Model , but Page Factory is much enhanced model. To start with, we just need to import package ‘org.openqa.selenium.support.PageFactory’

"Factory class can be used to make using Page Objects simpler and easier".

We use Page Factory pattern to initialize web elements which are defined in Page Objects.

We should initialize page objects using initElements() method from PageFactory Class as below, Once we call initElements() method, all elements will get initialized. PageFactory.initElements() static method takes the driver instance of the given class and the class type, and returns a Page Object with its fields fully initialized.

Home homePage = new HomePage(driver);
PageFactory.initElements(driver, homePage);

Or,

// To initialize elements.
HomePage homePage = PageFactory.initElements(driver, HomePage.class);

Or, as a constructor for page class as below:

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

We should preferably use a constructor which takes a WebDriver instance as its only argument or falling back on a no-arg constructor. An exception will be thrown if the class cannot be instantiated.

Page Factory will initialize every WebElement variable with a reference to a corresponding element on the actual web page based on “locators” defined. This is done by using @FindBy annotations.

Annotations?

In Page Factory, Annotations are used to give descriptive names for WebElements to improve code readability. And annotation @FindBy is used to identify Web Elements in the page.

By default, PageFactory will search for elements on the page with a matching id attribute, If that fails, then it will search by the name attribute. But as we need more control over identifying elements in the HTML page and mapping them to our Page Object fields. One way to do this is to use the @FindBy annotation, as shown in the following code:

The @FindBy annotation supports all the other locators strategies that we use:
id, name, className, css, xpath, tagName, linkText and partialLinkText

We can either use this annotation by specifying both "How" and "using" or by specifying any one of the location strategies (Eg: "id")

@FindBy(how = How.ID, using = "username") 
private WebElement userName;

We can re-write the above one as below:

@FindBy(id="username")
private WebElement userName;

And

To work with class name, we will define as below:

@FindBy(className=".input.username")
private WebElement userName;

When we have multiple elements (list of WebElements), we can initialize them using PageFactory as below :

@FindBy(tagName = "mylist") 
private List<WebElement> links;

Every time when a method is called on a WebElement, the driver will first find it on the current page and then simulate the action on the WebElement. There are cases where we will be working with a basic page, and we know that we will find the element on the page every time we look for it, In such cases we can use annotation ‘@CacheLookup‘ which is another annotation in page factory

@FindBy(name="username")
@CacheLookup
private WebElement userName;

What is @CacheLookup annotation in PageFactory?

We will mark annotation @CacheLookup to WebElements to indicate that it never changes (that is, that the same instance in the DOM will always be used)
CacheLookup attribute can be used to instruct the InitElements() method to cache the element once its located and so that it will not be searched over and over again – this is useful when the elements that are always going to be there
(For AJAX based applications, it may not work where the DOM changes based on user action on the page). Otherwise every time when we use a Web Element the WebDriver will go and search it again

But whenever we use @CacheLookup annotation, we will be losing one of the page factory benefit as it will find the element once and then keep a reference to it, hence, we are more likely to see StaleElementExceptions.

AjaxElementLocatorFactory

AjaxElementLocatorFactory is a lazy load concept in Page Factory pattern to identify WebElements only when they are used in any operation i.e. a timeOut for a WebElement can be assigned to the Object page class with the help of AjaxElementLocatorFactory.

Example:

/*** 
* Constructor 
* @param driver an instance of WebDriver 
*/
public int TimeoutValue = 30;
public SearchResultsPage(Webdriver driver) { 
    PageFactory.initElements(new AjaxElementLocatorFactory(driver, TimeoutValue), this);
}

The above code will wait for maximum of 30 seconds until the elements specified by annotations is loaded. If the element is not found in the given time interval, it will throw NoSuchElementException' exception.

Conclusion :

In fact, we can use the page object pattern without using the Page Factory class. The page object pattern simply abstracts business logic away from the physical structure of the pages. And the Page Factory class gives us the ability to use annotations which automatically find the elements on the page without specifying findElement.

Below is the sample code for Page Object Model in Selenium:

public class BasePage {

private By username = By.id("username");
private By password = By.id("password");
private By loginBtn = By.name("loginbtn");

  public void userLogin(String userName, String password) {
        driver.findElement(username).sendKeys("testuser");
        driver.findElement(password).sendKeys("testpassword");
        driver.findElement(loginBtn).click();
  }
}

And the below is the simple code written using Page Factory in Selenium:

public class BasePage {
  @FindBy(id= "username") private WebElement userName;
  @FindBy(id= "password") private WebElement password;
  @FindBy(id= "login") private WebElement loginBtn;

  public void userLogin(String userName, String password) {
    userName.sendKeys(userName);
    password.sendKeys(password);
    loginBtn.click();
  }
}
Selenium Tutorials: 

Comments

How to use multiple identifiers in Page Object Factory?
Like for radio button I want to use Name and Value ( Just for example)

You should be able to do using css selector with multiple attributes

Thanks for sharing the info, its really very helpful.

Add new comment