DEV Community

Cover image for Page Factory in Selenium For Web Automation Testing
himanshuseth004 for LambdaTest

Posted on • Updated on • Originally published at lambdatest.com

Page Factory in Selenium For Web Automation Testing

Automation testing has become an absolute necessity in an agile and fast-paced business environment with an immense focus on accelerated time to market. However, as far as automation is concerned, Selenium automation testing still reaps the maximum benefits in terms of test coverage and browser coverage.

As the product development progresses, automation testers are also on the war-footing to ensure that test scenarios are in line with features being implemented in the product. For example, if the tests are implemented without keeping scalability and maintainability in mind, minimal changes in the web application’s UI would change the Selenium test automation scripts.

The bigger challenge is to keep the test implementation ‘more decoupled’ from the web elements used in the test scripts. Page Object Model in Selenium, also called POM, is a popular design pattern in Selenium, used to create an object repository for storing WebElements.

In this blog on Selenium Page Factory tutorial, we deep dive into Page Factory in Selenium, which makes the usage of Page Objects simpler & easier through the factory class in Selenium WebDriver. Page Factory in Selenium can be considered the optimized Page Object Model (POM) for Selenium WebDriver.

Let’s look at what is Page Factory in Selenium along with deep-diving into the difference between Page Factory and Page Object Model in Selenium testing.

If you’re new to Selenium and wondering what it is then we recommend checking out our guide – What is Selenium?

What is Page Factory in Selenium

Page Factory is an inbuilt Page Object Model (POM) for Selenium WebDriver that is more optimized than POM. At a high level, Page Factory in Selenium can be considered as the extension of Page Objects. Page Factory class in Selenium is the catalyst that makes it easier and simpler to use Page Objects.

It is safe to mention that Page Factory is a combination of POM enabled through Page Factory Class in Selenium. Shown below is the top-level difference of Page Object Model (POM) and Page Factory:

Page Factory in Selenium

Though Page Factory in Selenium can be used in many ways, it surely helps improve the maintainability and re-usability aspects of the test implementation.

Introduction to PageFactory Class in Selenium

To support the Page Object (or PageObject) design pattern, Selenium WebDriver’s support library contains the PageFactory class that makes the usage of Page Objects simpler and easier. When using Page Factory in Selenium, automation testers can use the FindBy annotation for locating the web elements. These web elements are defined in web page classes (or Page Objects).

The major benefit of the @FindBy annotation is that it lets you initialize page elements without using the FindElement (or FindElements) in Selenium.

PageFactory class in Selenium also provides the initElements method for initializing the web elements. In further sections of this Selenium Page Factory tutorial, we deep dive into the most widely-used methods of the class. This will be further helpful when we demonstrate the Page Factory design pattern in Selenium using an automation example.

How to initialize Page Factory in Selenium

For using POM with Page Factory, the Page Objects (or page classes) containing the web elements (or element locators) for the page need to be initialized using relevant methods of the Page Factory class.

This is done to ensure that the web elements are initialized before any relevant actions are performed.
One of the Selenium best practices with Page Factory is to create all the web element variables at the beginning of the class and initialize those variables when the page is loaded.

The initialization of web elements can be done through the use of initElements methods of the PageFactory class. Here are some of the common annotations and methods that are widely used in Page Factory with Selenium.

FindBy annotation in Page Factory

The FindBy annotation is used to declare and initialize web element variables using the desired web locators in Selenium. Hence, you can find the web elements using popular locators like ID, Name, Link Text, Class Name, etc.

The first step is passing the attribute and its value (or element locator) for locating the web element to the @FindBy annotation. The second step is declaring the variable so that it can be used further in the test implementation.

There are two ways in which @FindBy annotation can be used:

Option 1

@FindBy(how = How.ID, using=" element-id")
private WebElement element-name;
Enter fullscreen mode Exit fullscreen mode
  • ‘How’ – The org.openqa.selenium.support.How class in Selenium provides Enum values (or contents) that signify the desired locator (e.g. CLASS_NAME, ID, CSS, ID_OR_NAME, NAME, LINK_TEXT, PARTIAL_LINK_TEXT, XPATH, TAG_NAME, etc.)

– ‘using’ is used for assigning value to the static variable

Option 2 [A more concise form]

@FindBy(id="element-id")
private WebElement element-name;
Enter fullscreen mode Exit fullscreen mode

In the syntax shown above, we have used the ID property for locating the element ‘element-id.’ Apart from ID, you can also use popular locators like CSS Selectors in Selenium, Name locator, Class Name locator, XPath locators, and more.

If there are multiple elements matching given locators, you can use FindAll annotation with multiple FindBy annotations.

@FindAll
   (
       {
           @FindBy(how = How.ID, using = "element"),
           @FindBy(className = "element-field")
       }
   )
private WebElement element_name;
Enter fullscreen mode Exit fullscreen mode

Example

We located the button with the link text ‘Start Free Testing’ on the LambdaTest homepage using the ‘Inspect tool’ in Google Chrome.

Page Factory in Selenium

Below are the two ways through which @FindBy annotation is used to declare the web element that was located using the XPath property.

Example: Option 1

Below are the two ways through which @FindBy annotation is used to declare the web element that was located using the XPath property.

Example: Option 1
Enter fullscreen mode Exit fullscreen mode

Example: Option 2

@FindBy(xpath = "//a[.='Start Free Testing']")
private WebElement SignUpButton;
Enter fullscreen mode Exit fullscreen mode

initElements method in Page Factory
Once the required web elements on the page are located using the appropriate web locators, the initElements method is used for declaring and initializing the requisite web element variables.

initElements is an overloaded function (or method) that can be used in multiple ways based on its arguments.

Here are some of the ways in which the web element variables can be initialized using the initElements method of Page Factory in Selenium:

Option 1: initElements
It creates an instance of the given class and sets a lazy proxy for each of the WebElements and List fields that have been declared earlier using the @FindBy annotation. An exception is thrown if the class cannot be instantiated.

Syntax

public static <T> T initElements(WebDriver driver,
                java.lang.Class<T> pageClassToProxy)
Enter fullscreen mode Exit fullscreen mode

Type Parameters

  • T – Class of the PageObject

Input Parameters

  • driver – Selenium WebDriver used for locating the web elements
  • pageClassToProxy – Class that will be initialized Returns

The method returns an instantiated instance of the class with WebElement and List fields that are proxied.

Example

SamplePage sample_page = PageFactory.initElements(web_driver, SamplePage.class);
Enter fullscreen mode Exit fullscreen mode

Option 2: initElements

It is largely similar to the initElements(WebDriver, Class) method except that it will replace the fields of an already instantiated object.

Syntax

static void initElements​(WebDriver driver, java.lang.Object page)
Enter fullscreen mode Exit fullscreen mode

Input Parameters

  • driver – Selenium WebDriver used for locating the web elements
  • page – Object with WebElement and List fields that have to be proxied.

Example

SamplePage sample_page = new SamplePage(web_driver);
PageFactory.initElements(web_driver, sample_page);
Enter fullscreen mode Exit fullscreen mode

Here we create an instance of the SamplePage class, and the created object is sample_page. The initElements method of the PageFactory class in Selenium initializes all the WebElements on the page SamplePage using @FindBy annotation.

The initialization of the web elements can also be done inside the web page class constructor:

public CreateAccountPage(WebDriver web_driver) {
       this.driver = web_driver;
       /* Initialize Elements */
       PageFactory.initElements(web_driver, this);
}
Enter fullscreen mode Exit fullscreen mode

Apart from the two above popular ways for initializing the web element variables when using Page Factory in Selenium, you can also use the following two approaches:

Option 3: initElements

static void initElements​(ElementLocatorFactory factory, java.lang.Object page)
Enter fullscreen mode Exit fullscreen mode

It is very similar to other initElements methods, the major difference is that it takes ElementLocatorFactory that provides the mechanism for finding the respective elements.

Option 4: initElements

static void initElements​(FieldDecorator decorator, java.lang.Object page)
Enter fullscreen mode Exit fullscreen mode

It is very much similar to other initElements methods, the major difference is that it takes a FieldDecorator used for decorating the fields.

An aerokube alternative to run tests upto 70% faster than any cloud grid, start your free test today!!!

Note- SHA256 Hash calculator - This free online tool helps you determine the integrity of your data and challenge hash authentication.

How To Initialize Web Elements In Page Factory in Selenium

As seen in the previous section of this Selenium Page Factory tutorial, the web element variables need to be initialized before any interaction can be performed on them. The initElements method is used for initializing the web elements in the web page classes or Page Objects.

Let’s take a simple example where we locate the desired web element using the appropriate web locators and initialize the same using initElements method. In this example, we locate the ‘Start Free Testing’ link using XPath in Selenium.

@FindBy(how = How.XPATH, using = "//a[.='Start Free Testing']")
@CacheLookup
private WebElement StartTestingButton;
Enter fullscreen mode Exit fullscreen mode

or

@FindBy(xpath = "//a[.='Start Free Testing']")
@CacheLookup
private WebElement StartTestingButton;
Enter fullscreen mode Exit fullscreen mode

Here, the web element StartTestingButton is cached using the @CacheLookup annotation. This annotation instructs Selenium to cache the web element instead of locating the element each time the web page is loaded.

Though @CacheLookup offers a significant amount of performance gains, it should only be used for static web elements (i.e., elements that do not change after each page reload).

The initElements method is used for initializing the web elements on the page.

public HomePage(WebDriver driver) {
       this.driver = driver;
       PageFactory.initElements(driver, this);
}
Enter fullscreen mode Exit fullscreen mode

We use the button click in Selenium to perform a click operation on the ‘Start Free Testing’ button using the XPath locator.

public void clickOnStartTestingButton()
{
       StartTestingButton.click();
}
Enter fullscreen mode Exit fullscreen mode

If you plan to use other web locators with Page Factory in Selenium, make sure to check out our detailed blog on using web locators with Page Factory.

What Is Lazy Initialization In Page Factory
So far in this Selenium Page factory tutorial, we have seen that the initElements method is used for initializing Page Objects of the Page Object class. However, does this necessarily mean that all the required WebElements on the page (or AUT) are located and stored at one shot?

Well, the elements are not located and stored beforehand. Selenium WebDriver will locate the respective WebElement only when that particular element (or Page Object) is in use or a relevant operation is performed on the said element.

In case the required Web Element is not available on the page, noSuchElementException Selenium exception is raised to indicate that the desired is not present in the DOM.

If you are intrigued to know about Selenium exceptions, check out our detailed blog on Exceptions in Selenium WebDriver.

To demonstrate how Page Factory in Selenium WebDriver performs Lazy Initialization, we navigate to the LambdaTest SignUp Page and try entering a ‘First Name’ into the Name field. However, the catch is that we have used an incorrect web locator to locate that element.

Page Factory in Selenium

Instead of the right XPath – //input[@name=’name’], we would be using the wrong XPath value (i.e. //input[@name=’names’]).

@FindBy(xpath = "//input[@name='names']")
WebElement full_name;
Enter fullscreen mode Exit fullscreen mode

Hence, the desired element is not located on the page, resulting in a WebDriver exception.

Page Factory in Selenium

Now that we have covered the essentials of Page Factory in Selenium, let’s get our hands dirty with a working test scenario. In this Selenium Page Factory example, the web pages are represented as Java classes.

The combination of @FindBy/@FindAll (or other annotations) and appropriate Selenium methods in the PageFactory class are used for locating web elements and initializing the same for Selenium automation testing.

With TestNG certification, you can challenge your skills in performing automated testing with TestNG and take your career to the next level.

Here’s a short glimpse of the TestNG certification from LambdaTest:

Demonstration: Page Factory in Selenium For Java
For this Selenium Page Factory Tutorial, we would be automating the LambdaTet signup process by navigating to the LambdaTest homepage and then entering the relevant details on the sign-up page.

The demonstration will be performed using the TestNG framework in Selenium. The execution is performed on the cloud-Selenium Grid provided by LambdaTest. Cloud testing with Selenium provides many benefits – improved test coverage (in terms of browser and OS combinations), accelerated time to market, and exceptional product quality. The demo for Page Factory in Selenium is performed on the Chrome (latest version) + Windows 10 combination for simplification.

The desired capabilities are generated using the desired capabilities generator on LambdaTest. A valid combination of user-name and access-key is required to access the LambdaTest cloud-grid, details of which are available in the LambdaTest profile section.

Test Scenario

  1. Locate the ‘Start Free Testing’ button on the LambdaTest page
  2. Click on the button
  3. Enter the details required for new registration
  4. Assert if the ‘Verify Your Email’ link does not open

Project Setup

We would be creating a Maven project in IntelliJ IDEA for demoing Page Factory in Selenium. Though we are using IntelliJ IDEA, you can use an IDE of your choice (e.g., Eclipse). In case you are getting started with IntelliJ or Eclipse for Java development, make sure to check out our blogs that help you with the following:

  • Configure Selenium with IntelliJ
  • Configure Selenium with Eclipse

Shown below is the directory structure:

Page Factory in Selenium

To get started, we have created a new package – com.pagefact under src/test/java folder. Under the newly created package, we have created two more packages:

  • com.pagefact.pages – Includes the Page Objects (or Page Classes) where the combination of @FindBy annotation and initElements method is used for preparing the backbone for the tests.

  • com.pagefact.tests – Includes the test implementation that will be using the respective page classes to perform relevant operations on the WebElements in the DOM.

With this, we are all set to create the required Page Object classes and the tests that would be using these page classes.

Implementation

Before we kick start the implementation, let’s add the required project dependencies in the project POM file.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>SeleniumTEST</groupId>
   <artifactId>PageObjectFactoryDemo</artifactId>
   <version>1.0-SNAPSHOT</version>
   <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
   <dependencies>
       <dependency>
           <groupId>org.testng</groupId>
           <artifactId>testng</artifactId>
           <version>6.9.10</version>
           <scope>test</scope>
       </dependency>
       <dependency>
           <groupId>org.seleniumhq.selenium</groupId>
           <artifactId>selenium-java</artifactId>
           <version>4.0.0-rc-2</version>
       </dependency>
       <dependency>
           <groupId>org.seleniumhq.selenium</groupId>
           <artifactId>selenium-chrome-driver</artifactId>
           <version>4.0.0-rc-2</version>
       </dependency>
       <dependency>
           <groupId>io.github.bonigarcia</groupId>
           <artifactId>webdrivermanager</artifactId>
           <version>4.1.0</version>
       </dependency>
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-nop</artifactId>
           <version>1.7.28</version>
           <scope>test</scope>
       </dependency>
   </dependencies>

   <build>
       <defaultGoal>install</defaultGoal>
       <plugins>
           <plugin>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>3.0</version>
               <configuration>
                   <source>1.8</source>
                   <target>1.8</target>
               </configuration>
           </plugin>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-surefire-plugin</artifactId>
               <version>2.12.4</version>
               <configuration>
                   <suiteXmlFiles>
                       <!-- TestNG suite XML files -->
                       <suiteXmlFile>testng.xml</suiteXmlFile>
                   </suiteXmlFiles>
               </configuration>
           </plugin>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-surefire-report-plugin</artifactId>
               <version>3.0.0-M5</version>
           </plugin>
       </plugins>
   </build>
</project>
Enter fullscreen mode Exit fullscreen mode

We are using the Selenium 4 RC (i.e., 4.0.0-rc-2) version for testing. In case you have not tried out Selenium 4 yet, make sure to check our detailed Selenium 4 tutorial that covers all the essential aspects like relative locators in Selenium 4, mocking geolocation with Selenium 4, Selenium Grid 4, etc. that are considered to be game-changers in Selenium 4.

TestNG (v 7.4.0) is used to implement, group, and test the respective scenarios. If you are new to the TestNG framework (or more well-versed with the JUnit framework), you can refer to JUnit vs TestNG comparison to understand the major differences between the two test automation frameworks.

Note- SHA1 Hash calculator - This free online tool helps you determine the checksum of a file or group of characters.

Page Objects (or Page Classes)

As mentioned earlier in this Selenium Page Factory tutorial, all the page classes are created under the package com.pagefact.pages

Page Factory in Selenium

HomePage Page Object

The very first page that we need to create is that of LambdaTest Homepage. For this, we create a HomePage class under com.pagefact.pages

package com.pagefact.pages;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import org.openqa.selenium.support.PageFactory;

public class HomePage {
   /* Local Selenium Grid */
   /* private WebDriver driver; */

   /* Remote Selenium Grid */
   private RemoteWebDriver driver;
   private static String PAGE_URL="https://www.lambdatest.com";

   @FindBy(how = How.XPATH, using = "//a[.='Start Free Testing']")
   private WebElement StartTestingButton;

   public HomePage(RemoteWebDriver driver)
   {
       this.driver = driver;
       driver.get(PAGE_URL);
       PageFactory.initElements(driver, this);
   }

   public void clickOnStartTestingButton()
   {
       StartTestingButton.click();
   }
}
Enter fullscreen mode Exit fullscreen mode

Code Walkthrough

Since we are running the tests on the LambdaTest cloud grid, the required packages are imported at the beginning of the implementation.

import org.openqa.selenium.remote.RemoteWebDriver;
Enter fullscreen mode Exit fullscreen mode

The required packages for using the Page Factory Selenium class and @FindBy annotation methods are also imported at the start.

import org.openqa.selenium.support.How;
import org.openqa.selenium.support.PageFactory;
Enter fullscreen mode Exit fullscreen mode

Since we are running the tests on the LambdaTest cloud grid, we create an instance of RemoteWebDriver in place of the local WebDriver. You can check out the Selenium RemoteWebDriver tutorial to get more insights into the difference between local and remote web drivers.

public class HomePage {
   private RemoteWebDriver driver;
Enter fullscreen mode Exit fullscreen mode

As we need to perform a click operation on the button ‘Start Free Testing,’ we locate the web element using the @FindBy annotation with the element’s XPath locator. You can use the ‘Inspect Tool’ in Chrome or a plugin like POM Builder or SelectorsHub in Chrome to get the XPath of the desired WebElement.

Page Factory in Selenium

A new web element variable (i.e. StartTestingButton) is created so that the applicable operations (i.e. click, sendKeys, etc.) can be performed on the element.

1
2
@FindBy(how = How.XPATH, using = "//a[.='Start Free Testing']")
private WebElement StartTestingButton;
Enter fullscreen mode Exit fullscreen mode

As discussed in the earlier section of this Selenium Page Factory tutorial, we initialize the web element variables in the constructor. Next, the URL is set to the LambdaTest homepage so that further tests can be carried out on the web element variables of that page.

public HomePage(RemoteWebDriver driver)
{
       this.driver = driver;
       driver.get(PAGE_URL);
       PageFactory.initElements(driver, this);
}
Enter fullscreen mode Exit fullscreen mode

Since the web element variable is a button, we create a method to provide a clicking mechanism on the web element variable (i.e., StartTestingButton).

public void clickOnStartTestingButton()
{
   StartTestingButton.click();
}
Enter fullscreen mode Exit fullscreen mode

SignUpPage Page Object

The purpose of this page class is to check whether the click on the ‘Start Free Testing’ is successful and the page has navigated to the ‘Account Creation page’. For this, we create a SignUpPage class under com.pagefact.pages

package com.pagefact.pages;

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

public class SignUpPage
{
   /* Local Selenium Grid */
   /* private WebDriver driver; */

   /* Remote Selenium Grid */
   private RemoteWebDriver driver;

   @FindBy(xpath = "//h1[@class='form_title']")
   private WebElement form_title;

   @FindBy(css = ".btn")
   private WebElement SignUpButton;

   public SignUpPage (RemoteWebDriver driver)
   {
       this.driver = driver;
       PageFactory.initElements(driver, this);
   }

   public boolean isLTPageOpen()
   {
       return form_title.getText().toString().contains("Signup for Free");
   }

}
Enter fullscreen mode Exit fullscreen mode

Code Walkthrough

There is only one method in this page class whose purpose is to check the web page’s title. So, first, we create a web element variable that is located using the @FindBy annotation and XPath locator in Selenium.

Page Factory in Selenium

A new web element variable ‘form_title’ is created, and the same will be used for cross-checking the title of the web page.

@FindBy(xpath = "//h1[@class='form_title']")
private WebElement form_title;
Enter fullscreen mode Exit fullscreen mode

A method named ‘isLTPageOpen’ is created that returns true if the page title contains the string ‘Signup for Free.’

public boolean isLTPageOpen()
{
   return form_title.getText().toString().contains("Signup for Free");
}
Enter fullscreen mode Exit fullscreen mode

CreateAccountPage Page Object

The purpose of this page class is to enter the requisite details in the ‘Account Creation page’ (where we are navigated after a click is performed on the ‘Start Free Testing’ button on the HomePage). For this, we create a CreateAccountPage class under com.pagefact.pages

package com.pagefact.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.Select;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
public class CreateAccountPage
{
   /* Local Selenium Grid */
   /* private WebDriver driver; */

   /* Remote Selenium Grid */
   private RemoteWebDriver driver;

   @FindBy(xpath = "//input[@name='name']")
   WebElement full_name;

   @FindBy(css = "[name='email']")
   WebElement org_email;

   @FindBy(xpath = "//input[@id='userpassword']")
   WebElement acc_password;

   @FindBy(xpath = "//input[@name='phone']")
   WebElement phone_number;

   @FindBy(css = "[name='designation']")
   WebElement designation;

   @FindBy(css = "[name='org_size']")
   WebElement org_size;

   @FindBy(xpath = "//samp[@class='customcheckbox']")
   WebElement check_box;

   @FindBy(css = ".btn")
   WebElement create_account_button;

   @FindBy(css = "#recaptcha-verify-button")
   @CacheLookup
   WebElement recaptcha_button;
   public CreateAccountPage(RemoteWebDriver driver)
   {
       this.driver = driver;
       PageFactory.initElements(driver, this);
   }
   public void setName(String fullname)
   {
       full_name.clear();
       full_name.sendKeys(fullname);
   }
   public void setEmail(String email)
   {
       org_email.clear();
       org_email.sendKeys(email);
   }
   public void setAccount_password(String pwd)
   {
       acc_password.clear();
       acc_password.sendKeys(pwd);
   }
   public void setDesignation(String desig_name) {
       Select dropdown;
       dropdown = new Select(designation);
       dropdown.selectByValue(desig_name);
   }
   public void setCompanySize(String comp_size) {
       Select dropdown;
       dropdown = new Select(org_size);
       dropdown.selectByValue(comp_size);
   }
   public void setPhone_number (String phonenum)
   {
       phone_number.clear();
       phone_number.sendKeys(phonenum);
   }
   public void clickAcceptTCButton()
   {
       check_box.click();
       driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
       create_account_button.click();
       driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
   }
   public boolean isVerifyPageOpen()
   {
       String expected_title = "Verify Your Email Address";
       String win_title = driver.getTitle();
       boolean isWinFound = win_title.indexOf(expected_title) != -1? true: false;
       return isWinFound;
   }
}
Enter fullscreen mode Exit fullscreen mode

Code Walkthrough

If the click operation on the ‘Start Free Testing’ button on the HomePage is successful, we will be navigated to the LambdaTest Account Creation page. The majority of the steps remain the same, as mentioned in the HomePage object creation section.

Since there are six fields on the ‘Account Creation’ page, the required web element variables are created by locating the WebElements using the appropriate web locator and @FindBy annotation.

Page Factory in Selenium

Here is how the element ‘Name’ is located using the XPath locator in Selenium.

@FindBy(xpath = "//input[@name='name']")
WebElement full_name;
Enter fullscreen mode Exit fullscreen mode

Since all the other web elements are text boxes, we use either CSS/XPath/any other relevant locator associated with @FindBy annotation. Once the element is located, corresponding web element variables (e.g., full_name, phone_number, acc_password, etc.) are created so that relevant actions can be performed on them.

@FindBy(xpath = "//input[@id='userpassword']")
WebElement acc_password;

@FindBy(xpath = "//input[@name='phone']")
WebElement phone_number;
Enter fullscreen mode Exit fullscreen mode

Once the required web elements are located, and web element variables are created, the initElements method is used for initializing the elements. As mentioned earlier in this Selenium Page Factory tutorial, the element(s) would be initialized only when appropriate action (e.g., click, sendKeys, clear, etc.) is performed on the same.

public CreateAccountPage(RemoteWebDriver driver)
{
       this.driver = driver;
       PageFactory.initElements(driver, this);
}
Enter fullscreen mode Exit fullscreen mode

Since the elements – Name, Business Email, Password, and Phone Number are text boxes, sendKeys in Selenium is used for entering the relevant information in those elements.

public void setName(String fullname)
 {
       full_name.clear();
       full_name.sendKeys(fullname);
}

public void setEmail(String email)
{
       org_email.clear();
       org_email.sendKeys(email);
}
Enter fullscreen mode Exit fullscreen mode

Designation and Company Size elements are drop-down boxes. Hence, we use appropriate methods to handle dropdown in Selenium. For example, we can select the required value from the drop-down(s) using selectByIndex(int), selectByValue(String), or other available methods.

public void setDesignation(String desig_name)
{
       Select dropdown;
       dropdown = new Select(designation);
       dropdown.selectByValue(desig_name);
}

public void setCompanySize(String comp_size)
{
       Select dropdown;
       dropdown = new Select(org_size);
       dropdown.selectByValue(comp_size);
}
Enter fullscreen mode Exit fullscreen mode

A method is created to select (or check) the ‘T&C Checkbox.’ Then, the element is located using the XPath selector.

Page Factory in Selenium

A blocking wait of 5 seconds is added to ensure the required operation is completed. Though it is not considered one of the Selenium best practices, we have used it only to demonstrate Page Factory’s usage in Selenium. You can also use explicit waits and fluent waits to add delays to handle dynamic web pages.

You can refer to our blog on waits in Selenium that deep dives into the integral aspects of handling dynamic web page content (with appropriate delays).

public void clickAcceptTCButton()
{
       check_box.click();
       driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
       create_account_button.click();
       driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
}
Enter fullscreen mode Exit fullscreen mode

Simple Selenium Test that uses the Page Classes

Now that we have created the required Page Objects (or page classes) let’s assemble all the pieces to create a simple Selenium test. The test file (i.e. SignUpOnLTTest.java) is created under the page com.pagefact.tests.

Page Factory in Selenium

Here is the implementation of the Selenium Test:

package com.pagefact.tests;

import com.pagefact.pages.*;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

/* TestNG framework is used instead of JUnit */
/* Tests triggered via testng.xml */
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class SignUpOnLTTest
{
   /* Local Selenium Grid */
   /* WebDriver driver; */
   /* Fetch details from https://accounts.lambdatest.com/detail/profile */
   public String username = "himanshu.sheth";
   public String accesskey = "IreQpTjhvTdZ9aFfouyQePZbOs4EPT3KpkqvUxxAWf4qGK9rvI";
   public String gridURL = "@hub.lambdatest.com/wd/hub";
   RemoteWebDriver driver = null;

   @BeforeClass
   public void testSetUp() throws MalformedURLException
   {
       /* Instantiate the Chrome instance */
       /* local Selenium Grid */
       /* driver = new ChromeDriver(); */
       /* Selenium 4 capabilities generated using https://www.lambdatest.com/capabilities-generator/ */
       ChromeOptions capabilities = new ChromeOptions();
       capabilities.setCapability("browserName", "chrome");
       capabilities.setCapability("version", "latest");
       capabilities.setCapability("platform", "Windows 10");
       capabilities.setCapability("build", "[Java] Page Factory Demo on LambdaTest");
       capabilities.setCapability("name", "[Java] Page Factory Demo on LambdaTest");
       capabilities.setCapability("geoLocation","IN");
       try
       {
           driver = new RemoteWebDriver(new URL("https://" + username + ":" + accesskey + gridURL), capabilities);
       }
       catch (MalformedURLException e)
       {
           System.out.println("Invalid grid URL");
       }
       catch (Exception e)
       {
           System.out.println(e.getMessage());
       }
       driver.manage().window().maximize();
       driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
   }

   @Test
   public void test_signupOnLambdaTest() throws InterruptedException
   {
       HomePage home = new HomePage(driver);
       home.clickOnStartTestingButton();

       SignUpPage signup = new SignUpPage(driver);

       /* Check if page is opened */
       Assert.assertTrue(signup.isLTPageOpen());

       /* Create object of CreateAccountPage */
       CreateAccountPage accountPage =new CreateAccountPage(driver);

       /* Fill up data */
       accountPage.setName("Page Factory Testing");
       accountPage.setEmail("pagefactorytesting123@gmail.com");
       accountPage.setAccount_password("password123");
       accountPage.setPhone_number("911234567891");
       accountPage.setDesignation("Manager");
       accountPage.setCompanySize("10000+");

       /* Click on Accept T&C and Create Account button */
       accountPage.clickAcceptTCButton();

       /* Wait for a few seconds */
       driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));

       /* Check if page is opened */
       if (accountPage.isVerifyPageOpen())
       {
           System.out.println("Email address verification page is open\n");
       }

       /* Wait for a few seconds */
       driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));

       System.out.println("Page Factory demo complete\n");
   }

   @AfterClass
   public void tearDown()
   {
       System.out.println("Close method of WebDriver Called");
       driver.close();
   }
}
Enter fullscreen mode Exit fullscreen mode

Code Walkthrough

To get started with the Selenium test, we import all the Page Objects (or Page Classes) that are available in the com.pagefact.pages package.

import com.pagefact.pages.*;
Enter fullscreen mode Exit fullscreen mode

The desired capabilities [i.e., Chrome (latest) on Windows 10] are generated using the Desired Capabilities Generator on LambdaTest. The geolocation is set to “IN” (i.e., the site will be tested with the desired location set to India). Geolocation testing in Selenium is a must for websites (or web apps) that target a global audience.

Page Factory in Selenium

Since cross browser testing is performed on Selenium 4 Grid, hence relevant browser options (i.e., ChromeOptions) are used instead of Desired Capabilities (which is used in Selenium 3).

ChromeOptions capabilities = new ChromeOptions();
capabilities.setCapability("browserName", "chrome");
capabilities.setCapability("version", "latest");
capabilities.setCapability("platform", "Windows 10");
capabilities.setCapability("build", "[Java] Page Factory Demo on LambdaTest");
capabilities.setCapability("name", "[Java] Page Factory Demo on LambdaTest");
capabilities.setCapability("geoLocation","IN");
Enter fullscreen mode Exit fullscreen mode

A new instance of Remote WebDriver is created with the browser & OS capabilities set in the earlier step. Then, the combination of user-name and access-key is used to access LambdaTest Selenium Grid (i.e., @hub.lambdatest.com/wd/hub).

driver = new RemoteWebDriver(new URL("https://" + username + ":" + accesskey + gridURL), capabilities);
Enter fullscreen mode Exit fullscreen mode

The newly created instance of Remote ChromeDriver instance is used across the Objects of the Page Classes (that were created in the com.pagefact.pages package). The Remote ChromeDriver initialization is a part of the testSetUp() method implemented under the @BeforeClass TestNG annotation. TestNG annotations in Selenium aid in building a more robust framework and provide additional information about the corresponding class or method.

@BeforeClass
public void testSetUp() throws MalformedURLException
{
Enter fullscreen mode Exit fullscreen mode

In this Page Factory in Selenium demonstration, we have created a single test method (i.e., test_signupOnLambdaTest) implemented under the @test annotation. You can also use the TestNG parameterized test to run the same test (or separate tests) across different input combinations (e.g., different browsers and platforms).

@Test
public void test_signupOnLambdaTest() throws InterruptedException
{
Enter fullscreen mode Exit fullscreen mode

We first create an object of the HomePage page class. Then, the handle of the Remote WebDriver passes an argument to the instance of each Page Object, as the same WebDriver instance has to be used across all the test method(s).

The clickOnStartTestingButton() method of the HomePage class is invoked to perform a click on the ‘Start Free Testing’ button.

Page Factory in Selenium

HomePage home = new HomePage(driver);
home.clickOnStartTestingButton();
Enter fullscreen mode Exit fullscreen mode

If the previous step is successful, we will be navigated to the ‘Account SignUp’ page. Here we create an instance of the SignUpPage page class, post which we check if the page title contains ‘Signup for Free.’

SignUpPage signup = new SignUpPage(driver);
Assert.assertTrue(signup.isLTPageOpen());
Enter fullscreen mode Exit fullscreen mode

The final step to account creation on LambdaTest involves creating an instance of the CreateAccountPage page object. Here, we pass the respective details (e.g., full name, email, phone number, etc.) to the account creation page.

Page Factory in Selenium

The respective methods [i.e. setName(), setEmail(), setAccount_password(), etc.) are invoked to populate the data to the corresponding WebElements on the page.

CreateAccountPage accountPage = new CreateAccountPage(driver);
accountPage.setName("Page Factory Testing");
accountPage.setEmail("pagefactorytesting123@gmail.com");
accountPage.setAccount_password("password123");
accountPage.setPhone_number("911234567891");
accountPage.setDesignation("Manager");
accountPage.setCompanySize("10000+");
accountPage.clickAcceptTCButton();
Enter fullscreen mode Exit fullscreen mode

The test is considered passed if the ‘Verify your email’ page appears on the screen.

if (accountPage.isVerifyPageOpen())
{
  System.out.println("Email address verification page is open\n");
}
Enter fullscreen mode Exit fullscreen mode

Page Factory in Selenium

Running the Page Factory in Selenium demo test

Since we have not implemented a parameterized TestNG test, testng.xml contains the bare minimum information to invoke the tests under the com.pagefact.tests package.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite">
   <test verbose="2" preserve-order="true" name="Page Factory Demo">
       <classes>
           <class name="com.pagefact.tests.SignUpOnLTTest">
               <methods>
                   <include name="test_signupOnLambdaTest"/>
               </methods>
           </class>
       </classes>
   </test>
</suite>
Enter fullscreen mode Exit fullscreen mode

Since the tests are running on the cloud Selenium Grid on LambdaTest, hop on to the LambdaTest Automation Dashboard to check the test execution status. As shown below, the test execution was successful.

Page Factory in Selenium

Note- SHA512 Hash calculator - This program helps you determine the integrity of your data and challenge hash authentication.

It’s A Wrap

Image descriptionSource

Page Factory and Page Object Model (POM) are design patterns that make it easy for QA engineers to maintain and re-use code in a more effective manner. Page Factory in Selenium provides more firepower to POM due to the support of important annotations and methods that simplify locating and initializing the web element variables.

In this Selenium Page Factory tutorial, we have seen how Page Factory in Selenium can be used effectively for agile projects, as Page Object classes can be reused across relevant test suites by reducing maintenance and implementation efforts. Furthermore, akin to POM, the user interactions are abstracted from the core test implementation, making it easy to adapt to the changing project requirements.

Do let us know how you leveraged Page Factory in Selenium test suites for expediting test suite delivery.

Happy Testing!

Top comments (0)