Page Object Model with Page Factory in Selenium

HOME

What Is Page Object Model (POM)?

Page Object model is an object design pattern in Selenium, where web pages are represented as classes, and the various elements on the page are defined as variables in the class and all possible user interactions can then be implemented as methods in the class.

The benefit is that if there is any change in the UI for the page, the tests themselves don’t need to change, only the code within the page object needs to change. Subsequently all changes to support that new UI are located in one place.

The Page Object Design Pattern provides the following advantages:

1. Readable – There is a clean separation between test code and page specific code such as locators and methods.

2. Maintainability  -In this model, separate classes are created for different pages of a web application like login page, the home page, employee detail page, change password page, etc. So, if there is any change in any element of a website then we only need to make changes in one class, and not in all classes.

3. Reusable – If multiple test scripts use the same web elements, then we need not write code to handle the web element in every test script. Placing it in a separate page class makes it reusable by making it accessible by any test script.

4. Easy project Structure – Its project structure is quite easy and understandable.

5. PageFactory – It can use PageFactory in the page object model in order to initialize the web element and store elements in the cache.

In case there are lots of web elements on a page, then the object repository class for a page can be separated from the class that includes methods for the corresponding page.

 Example: If the New Customer page has many input fields. In that case, there can be 2 different classes. One class called NewCustomerObjects.java that forms the object repository for the UI elements on the register accounts page.

A separate class file NewCustomerMethods.java extending or inheriting NewCustomerObjects that includes all the methods performing different actions on the page could be created.

Consider the below script to login to an application and navigate to home page.

This is a small script. Therefore, script maintenance and readability looks very easy.

Imagine there are 50 different tests present in this script. In that case, the readability of the script decreases as well as maintenance become very difficult.

Scenario

  1. Launch the Firefox browser.
  2. The demo website opens in the browser.
  3. Verify the Login Page
  4. Enter username and Password and login to the demo site.
  5. Verify the home page.
  6. Close the browser.
package PageObjectModel;

import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

public class NonPOMExample {

     WebDriver driver;

     @BeforeTest
     public void setup() {

           System.setProperty("webdriver.gecko.driver", "src\\test\\resources\\webdrivers\\window\\geckodriver.exe");
           driver = new FirefoxDriver();
           driver.manage().window().maximize();
           driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
           driver.get("https://opensource-demo.orangehrmlive.com/");
     }

     @Test(priority = 0)
     public void Login() {
           String pageTitle = driver.findElement(By.id("logInPanelHeading")).getText();
           Assert.assertTrue(pageTitle.contains("LOGIN Panel"));
     }

     @Test(priority = 1)
     public void HomePage() {

           driver.findElement(By.name("txtUsername")).sendKeys("Admin");
           driver.findElement(By.name("txtPassword")).sendKeys("admin123");
           driver.findElement(By.id("btnLogin")).submit();
           String homePageText = driver.findElement(By.id("welcome")).getText();
           Assert.assertTrue(homePageText.contains("Welcome"));
     }

     @AfterTest
     public void close() {
           driver.close();
     } 
}

What Is Pagefactory?

PageFactory is a way of implementing the “Page Object Model”. Here, we follow the principle of separation of Page Object Repository and Test Methods. It is an inbuilt concept of Page Object Model which is very optimized.

1. The annotation @FindBy is used in Pagefactory to identify an element while POM without Pagefactory uses the driver.findElement() method to locate an element.

2. The second statement for Pagefactory after @FindBy is assigning an <element name> of type WebElement class that works exactly similar to the assignment of an element name of type WebElement class as a return type of the method driver.findElement() that is used in usual POM (userName in this example).

Non POM

driver.findElement(By.name("txtUsername"));

POM

@FindBy(name = "txtUsername")
WebElement userName;

3. Below is a code snippet of non PageFactory Mode to set Firefox driver path. A WebDriver instance is created with the name driver and the FirefoxDriver is assigned to the ‘driver’.  The same driver object is then used to launch the demo website, locate the webelements and to perform various operations

Basically, here the driver instance is created initially and every web element is freshly initialized each time when there is a call to that web element using driver.findElement() or driver.findElements().

 System.setProperty("webdriver.gecko.driver", "src\\test\\resources\\webdrivers\\window\\geckodriver.exe");
          driver = new FirefoxDriver();
           driver.manage().window().maximize();
           driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
           driver.get("https://opensource-demo.orangehrmlive.com/");

But with POM with PageFactory approach, all the elements are initialized with initElements() without explicitly initializing each web element.

The initElements is a static method of PageFactory class which is used to initialize all the web elements located by @FindBy annotation. Thus, instantiating the Page classes easily. It is used to initialize the WebElements declared, using driver instance from the main class. In other words, WebElements are created using the driver instance. Only after the WebElements are initialized, they can be used in the methods to perform actions.

public Login(WebDriver driver) {
           this.driver = driver;
           // This initElements method will create all WebElements
           PageFactory.initElements(driver, this);
     }

Steps To Create POM With Page Factory Real-Time Example

Step 1 – Create a Maven Project

Click here to know How to create a Maven project

Step 2 – Add dependencies to the pom.xml

<dependencies>
   
   <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>3.141.59</version>
    </dependency>
     
    <!-- https://mvnrepository.com/artifact/org.testng/testng -->
    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>7.4.0</version>
      <scope>test</scope>
    </dependency>   
     
</dependencies>

Step 3 – Create a Java Class for each page:

In this example, we will access 2 web pages, “Login” and “Home” pages.

Hence, we will create 2 Java classes in Page Layer  –  Login.java and HomePage.java

3.1. Define WebElements as variables using Annotation @FindBy:

We would be interacting with:

  • Message on Login Page, Username, Password, Login button field on the Login Page.
  • Successful message on the Home Page

For Example: If we are going to identify the Username using attribute name, then its variable declaration is

 @FindBy(name = "txtUsername")
 WebElement userName;

3.2 Create methods for actions performed on WebElements.

Below actions are performed on WebElements in Login Page:

  • Get Text on Login Page
  • Type action on the Username field.
  • Type action in the Password field.
  • Click action on the Login Button

Note: A constructor has to be created in each of the class in the Page Layer, in order to get the driver instance from the Main class in Test Layer and also to initialize WebElements(Page Objects) declared in the page class using PageFactory.InitElement().

Login Page

package PageObjectModel;

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

public class Login {

     /**
      *
      * All WebElements are identified by @FindBy annotation
      *
      */
     WebDriver driver;

     @FindBy(name = "txtUsername")
     WebElement userName;

     @FindBy(name = "txtPassword")
     WebElement password;

     @FindBy(id = "logInPanelHeading")
     WebElement titleText;

     @FindBy(id = "btnLogin")
     WebElement login;

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

           // This initElements method will create all WebElements
           PageFactory.initElements(driver, this);
     }

     // Set user name in textbox
     public void setUserName(String strUserName) {
           userName.sendKeys(strUserName);
     }

     // Set password in password textbox
     public void setPassword(String strPassword) {
           password.sendKeys(strPassword);
     }

     // Click on login button
     public void clickLogin() {
           login.click();
     }

     // Get the title of Login Page
     public String getLoginTitle() {
           return titleText.getText();
     }

     public void login(String strUserName, String strPasword) {

           // Fill user name
           this.setUserName(strUserName);

           // Fill password
           this.setPassword(strPasword);

           // Click Login button
           this.clickLogin();
     }
}

HomePage. java

package PageObjectModel;

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

public class HomePage {
     WebDriver driver;

     @FindBy(id = "welcome")
     WebElement homePageUserName;

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

           // This initElements method will create all WebElements
           PageFactory.initElements(driver, this);
     }

     // Get the User name from Home Page
     public String getHomePageText() {
           return homePageUserName.getText();
     }
}

Step 4 –  Create test class for the tests of these pages – POMTest

POMTest.java

package PageObjectModel;

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

public class POMTest {

     WebDriver driver;
     Login objLogin;
     HomePage objHomePage;
 
     @BeforeTest
     public void setup() {
          System.setProperty("webdriver.gecko.driver", "src\\test\\resources\\webdrivers\\window\\geckodriver.exe");
           driver = new FirefoxDriver();
           driver.manage().window().maximize();
           driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
           driver.get("https://opensource-demo.orangehrmlive.com/");
     } 

     /**
      *
      * This test go to https://opensource-demo.orangehrmlive.com/ Verify login page
      * title as LOGIN Panel Login to application Verify the home page using welcome
      * message
      *
      */

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

           // Create Login Page object
           objLogin = new Login(driver);

           // Verify login page text
           String loginPageTitle = objLogin.getLoginTitle();
           Assert.assertTrue(loginPageTitle.contains("LOGIN Panel"));
     }

     @Test(priority = 1)
     public void HomeTest() {

           // login to application
           objLogin.login("Admin", "admin123");

           // go the next page
           objHomePage = new HomePage(driver);

           // Verify home page
           Assert.assertTrue(objHomePage.getHomePageText().contains("Welcome"));
     }

     @AfterTest
     public void close() {
           driver.close();
     }
}

Step 5 – To run the test, right click and select as Run As and then select TestNG Test.

Step 6 – Create TestNG.xml

You can add TestNG.xml and run the tests from there also.

<?xml version = "1.0"encoding = "UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name = "PageObjectModel">
  <test name = "PageObjectModel Tests">
    <classes>
          <class name = "com.example.pageobjectmodel.tests.POMTest"/>
     </classes>  
   </test>
</suite>

Step 7 – Run the tests from TestNG.xml

Right click on TestNG.xml and select Run As TestNG Suite.

The execution status looks like as shown below.

Step 8 -TestNG Report Generation

Once the execution is finished, refresh the project. It will create a test-output folder containing various reports generated by TestNG. Below is the screenshot of the report folder.

Image of Index.html report

Image of emailable-report.html

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s