As we continue to automate the test cases on a daily basis using Selenium automation, the maintenance of the growing test suite parallely becomes complicated. While automating the test cases on Selenium, there would be an instance where we use the same web element in multiple test scripts. For example, while automating an E-Commerce application, in every test case we have to search for a particular item from a search field using Selenium locator i.e. using XPath or ID. For such scenarios, we add the search field locator in every test script which would eventually increase code duplicacy.

Moreover, if there is any change in the locator, we have to change the locator in every test script where a particular web element is used. To overcome such challenges, we would be further looking at the Page Object Model that would help build a framework that is easy to use and maintain.

What is the Page Object Model (POM)?

Page Object Model is a design pattern commonly used in test automation for creating an Object Repository of web elements. The primary advantage of adopting POM is to reduce code duplicacy and to reduce maintenance efforts.

To start with, a Page Object Model framework development, we create different classes for different web pages. Each class file contains a web element locator for the elements present on a particular web page. Multiple test scripts can further use these element locators to perform various web actions. Since a separate class file is created for each web page and the common locators can be used by multiple test classes, this reduces the code duplicity and improves code maintenance.

Advantages of Page Object Model

  • Enhances code maintainability : In case of changes in the locators, updating the code becomes very easy as web element locators are maintained separately from the test classes.
  • Enhances code readability : With the elimination of code duplicacy and redundancy from test classes, the code becomes more clean and easy to read.
  • Enhances code reusability : Creating a separate object repository allows multiple test scripts to access required locators from a single object repository based on web pages. This type of design pattern enhances code reusability.

Implementing Page Object Model with Selenium

Now since we are aware of the Page Object Model design pattern, let’s implement a basic structure of Page Object Model with Selenium framework where all the web elements of the web page and the method to perform web actions will be maintained in a class file.

Further, these web action methods will be called in selenium based test scripts and assertions will also be applied in test methods. As an example, let’s automate the below login test scenario:

  1. Direct to pCloudy login page
  2. Enter username and password
  3. Click login
  4. Validate the web page title

pCloudyLogin Page POM 

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class pCloudyLogin {

  WebDriver driver;
 
  By loginMenuButton = By.xpath("//a[text()='Login']");
  By userID = By.id("userId");
  By passWord = By.name("password");
  By loginButton = By.id("loginSubmitBtn");
 
  public pCloudyLogin(WebDriver driver){
        this.driver = driver;
    }
 
  public void clickLoginHeaderButton()
  {
  driver.findElement(loginMenuButton).click();
  }
 
  public void setUserID(String username)
  {
  driver.findElement(userID).sendKeys(username);
  }
 
  public void setPassword(String password)
  {
  driver.findElement(passWord).sendKeys(password);
  }
 
  public void clickLoginButton()
  {
  driver.findElement(loginButton).click();
  }
 
  public String getLoginTitle(){

  return driver.getTitle();
  }
 
  public void loginToPCloudy(String username, String password)
  {
  this.setUserID(username);
  this.setPassword(password);
  this.clickLoginButton();
  }
 
}

TestLogin Selenium Test Case

import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import io.github.bonigarcia.wdm.WebDriverManager;

public class TestLogin {

    WebDriver driver;
pCloudyLogin objLogin;

@BeforeTest
public void setup() {

WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().window().maximize();

driver.get("https://www.pcloudy.com/");
}

@Test(priority=0)
public void testPCloudyLogin(){

objLogin = new pCloudyLogin(driver);

objLogin.clickLoginHeaderButton();
    objLogin.loginToPCloudy("ramit.dhamija@gmail.com", "ramit9876");
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
       
    String expectedTitle = "Mobile App Testing, Continuous Testing Cloud, Mobile Testing Tools | pCloudy";
    String actualTitle = objLogin.getLoginTitle();

        Assert.assertEquals(actualTitle,expectedTitle, "pCloudy Login Failed");
}

@AfterTest
public void tearDown() {

if(driver!=null)
{
driver.quit();
}
}

}

Code Walkthrough :

As per the Page Object Model design pattern, we have created a separate class named “pCloudyLogin” that behaves as an object repository for a login web page. The locators used to login to the pCloudy system have been added in this class and for usage of each locator, a different method has been created to perform web action so that the same methods can be used in multiple test scripts.

Next, we have created a test class where all the methods of web actions are called step by step in a test method. In the BeforeTest annotated method we have set up the ChromeDriver and launched the automated browser. In the test method, along with the calling of the web action method, we have passed the test data to the required methods and have added assertion to validate the login test case using the web page title. In the AfterTest annotated method, we have quit the Selenium session/browser.

Note: For webdriver setup and teardown activity, a separate class can be created to enhance code maintainability and readability.

What is the Page Factory?

Page Factory is a Selenium inbuilt class to support Page Object Design pattern. With Page Factory, the framework can be built in a more optimized form.

It provides a @FindBy annotation to find web elements without using “FindElement/s”.

The initElements method can be further used to initialize web elements in Page Class.

With the Page Factory Model, we use the same concept of maintaining the object repository separating from the test class.

@FindBy annotation accepts Selenium supported locators as parameters, for example : id, xpath, name, css, tagName, linkText, partialLinkText etc.

initElements is a static method of PageFactory class that accepts two parameters i.e. WebDriver and reference to the current class.

AjaxElementLocatorFactory is used to locate web elements only when the elements are utilized in any operation and is known as lazy load concept of Page Factory in Selenium. The web element timeout can be assigned to Object Class using AjaxElementLocatorFactory. Example of AjaxElementLocatorFactory:

AjaxElementLocatorFactory factory = new 
AjaxElementLocatorFactory(driver,100);
PageFactory.initElements(driver, this);

With the above code example, If the element is not visible to perform an operation in 100 seconds, a timeout exception will appear.

Implementing Page Factory with Selenium

Now since we are clear with Page Factory, let’s have a look on implementing Page Factory with Selenium. As an example, let’s automate the below login test scenario:

  1. Direct to pCloudy login page
  2. Enter username and password
  3. Click login
  4. Validate the web page title

pCloudyLogin Page Class

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class pCloudyLogin {

  WebDriver driver;
 
  @FindBy(xpath="//a[text()='Login']")
  WebElement loginMenuButton;
 
  @FindBy(id="userId")
  WebElement userID;
 
  @FindBy(name="password")
  WebElement passWord;
 
  @FindBy(id="loginSubmitBtn")
  WebElement loginButton;
 
  public pCloudyLogin(WebDriver driver){
 
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }
 
  public void clickLoginHeaderButton()
  {
  loginMenuButton.click();
  }
 
  public void setUserID(String username)
  {
  userID.sendKeys(username);
  }
 
  public void setPassword(String password)
  {
  passWord.sendKeys(password);
  }
 
  public void clickLoginButton()
  {
  loginButton.click();
  }
 
  public String getLoginTitle(){

  return driver.getTitle();
  }
 
  public void loginToPCloudy(String username, String password)
  {
  this.setUserID(username);
  this.setPassword(password);
  this.clickLoginButton();
  }
 
}

TestLogin Selenium Test Case

import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import io.github.bonigarcia.wdm.WebDriverManager;

public class TestLogin {

    WebDriver driver;
pCloudyLogin objLogin;

@BeforeTest
public void setup() {

WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().window().maximize();

driver.get("https://www.pcloudy.com/");
}

@Test(priority=0)
public void testPCloudyLogin(){

objLogin = new pCloudyLogin(driver);

objLogin.clickLoginHeaderButton();
    objLogin.loginToPCloudy("ramit.dhamija@gmail.com", "ramit9876");
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
       
String expectedTitle = "Mobile App Testing, 
Continuous Testing Cloud, Mobile Testing Tools | pCloudy";

    String actualTitle = objLogin.getLoginTitle();

Assert.assertEquals(actualTitle,expectedTitle, "pCloudy Login Failed");
}

@AfterTest
public void tearDown() {

if(driver!=null)
{
driver.quit();
}
}

}

Code Walkthrough :

In the login page class, we have defined all the web elements required to validate login test cases. @FindBy annotation provided by Selenium to support Page Factory Model is used to find defined web elements. An initElements() method has been used to initialize web elements. For each web element defined, we have created a method to perform web actions on web elements.

Next, we have created a test class where all the methods for web actions are called step by step in a test method. In the BeforeTest annotated method we have set up the ChromeDriver and launched the automated browser. In the test method, along with the calling of the web action method, we have passed the test data to the required methods and have added assertion to validate the login test case using the web page title. In the AfterTest annotated method, we have quit the Selenium session/browser.

Note: For webdriver setup and teardown activity, a separate class can be created to enhance code maintainability and readability.

Conclusion

Knowing how to use Page Object Model and Page Factory in Selenium can be a huge benefit to many developers and automation engineers who struggle with duplicity of test cases. With the help of POM and Page factory in Selenium automation engineers can now separate out the web elements to easily locate them on test scripts to perform various web actions. This will go a long way in code reusability, maintenance and elimination of duplication for future test cases. 

exception in selenium