What is a Page Object Model?
Page Object Model (POM) is a design pattern, popularly used in test automation that creates Object Repository for web UI elements. The advantage of the model is that it reduces code duplication and improves test maintenance.
Under this model, for each web page in the application, there should be a corresponding Page Class. This Page class will identify the WebElements of that web page and also contains Page methods which perform operations on those WebElements. Name of these methods should be given as per the task they are performing, i.e., if a loader is waiting for the payment gateway to appear, POM method name can be waitForPaymentScreenDisplay().
Why Page Object Model?
Starting an UI Automation in Selenium WebDriver is NOT a tough task. You just need to find elements, perform operations on it.
As you can observe, all we are doing is finding elements and filling values for those elements.
This is a small script. Script maintenance looks easy. But with time the test suite will grow. As you add more and more lines to your code, things become tough.
The chief problem with script maintenance is that if 10 different scripts are using the same page element, with any change in that element, you need to change all 10 scripts. This is time consuming and error prone.
A better approach to script maintenance is to create a separate class file which would find web elements, fill them or verify them. This class can be reused in all the scripts using that element. In the future, if there is a change in the web element, we need to make the change in just 1 class file and not 10 different scripts.
This approach is called Page Object Model(POM). It helps make the code more readable, maintainable, and reusable.
Advantages of POM
- Page Object Pattern says operations and flows in the UI should be separated from verification. This concept makes our code cleaner and easy to understand.
- The Second benefit is the object repository is independent of test cases, so we can use the same object repository for a different purpose with different tools. For example, we can integrate POM with TestNG/JUnit for functional Testing and at the same time with JBehave/Cucumber for acceptance testing.
- Code becomes less and optimized because of the reusable page methods in the POM classes.
- Methods get more realistic names which can be easily mapped with the operation happening in UI. i.e. if after clicking on the button we land on the home page, the method name will be like ‘gotoHomePage()’.
How to implement POM?
Simple POM:
It’s the basic structure of Page object model (POM) where all Web Elements of the AUT and the method that operate on these Web Elements are maintained inside a class file.A task like verification should be separate as part of Test methods.
Here are we are dealing with 2 pages
- Login Page
- Home Page (shown once you login)
Accordingly, we create 2 POM classes
Implementation Of Simple POM In Selenium
Scenario To Automate
Now we automate the given scenario using the Page Object Model.
The scenario is explained below:
- Step 1: Launch the site “ https: //demo.vtiger.com ”.
- Step 2: Enter the valid credential.
- Step 3: Login to the site.
- Step 4: Verify the Home page.
- Step 5: Logout the site.
- Step 6: Close the Browser.
Selenium Scripts For The Above Scenario In POM
Now we create the POM Structure in Eclipse, as explained below:
Step 1: Create a Project in Eclipse – POM based Structure:
Create Project “ Page Object Model ”.
Create Project – Page Object Model
Create 3 Package under the project.
- library
- pages
- test cases
Library: Under this, we put those codes that need to be called again and again in our test cases like Browser launch, Screenshots, etc. The user can add more classes under it based on the project need.
Pages: Under this, classes are created for each page in the web application and can add more page classes based on the number of pages in the application.
Test cases: Under this, we write the login test case and can add more test cases as required to test the whole application.
POM – SRC
- Classes under the Packages are shown in the below image.
Classes under the Packages
Step 2: Create the following classes under the library package.
Browser.java: In this class, 3 browsers ( Firefox, Chrome and Internet Explorer ) are defined and it is called in the login test case. Based on the requirement, the user can test the application in different browsers as well.
- package library;
- import org.openqa.selenium.WebDriver;
- import org.openqa.selenium.chrome.ChromeDriver;
- import org.openqa.selenium.firefox.FirefoxDriver;
- import org.openqa.selenium.ie.InternetExplorerDriver;
- public class Browser {
- static WebDriver driver;
- public static WebDriver StartBrowser(String browsername , String url)
- {
- // If the browser is Firefox
- if(browsername.equalsIgnoreCase(“Firefox”))
- {
- // Set the path for geckodriver.exe
- System.setProperty(“webdriver.firefox.marionette”,” E://Selenium//Selenium_Jars//geckodriver.exe “);
- river = new FirefoxDriver();
- }
- // If the browser is Chrome
- else if(browsername.equalsIgnoreCase(“Chrome”))
- {
- // Set the path for chromedriver.exe
- System.setProperty(“webdriver.chrome.driver”,”E://Selenium//Selenium_Jars//chromedriver.exe”);
- driver = new ChromeDriver();
- }
- // If the browser is IE
- else if(browsername.equalsIgnoreCase(“IE”))
- {
- / Set the path for IEdriver.exe
- System.setProperty(“webdriver.ie.driver”,”E://Selenium//Selenium_Jars//IEDriverServer.exe”);
- driver = new InternetExplorerDriver();
- }
- driver.manage().window().maximize();
- driver.get(url);
- return driver;
- }
- }
ScreenShot.java: In this class, a screenshot program is written and it is called in the test case when the user wants to take a screenshot of whether the test fails or passes.
- package library;
- import java.io.File;
- import org.apache.commons.io.FileUtils;
- import org.openqa.selenium.OutputType;
- import org.openqa.selenium.TakesScreenshot;
- import org.openqa.selenium.WebDriver;
- public class ScreenShot {
- public static void captureScreenShot(WebDriver driver, String ScreenShotName)
- {
- try {
- File screenshot=((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
- FileUtils.copyFile(screenshot,new File(“E://Selenium//”+ScreenShotName+”.jpg”));
- } catch (Exception e)
- {
- System.out.println(e.getMessage());
- e.printStackTrace();
- }
- }
Step 3 : Create page classes under Page package.
HomePage.java: This is the Home page class, in which all the elements of the home page and methods are defined.
- package pages;
- import org.openqa.selenium.By;
- import org.openqa.selenium.WebDriver;
- public class HomePage {
- WebDriver driver;
- By logout = By.id(“p_lt_ctl03_wSOB_btnSignOutLink”);
- By home = By.id(“p_lt_ctl02_wCU2_lblLabel”);
- //Constructor to initialize object
- public HomePage(WebDriver dr)
- {
- this.driver=dr;
- }
- public String pageverify()
- {
- return driver.findElement(home).getText();
- }
- publicvoid logout()
- {
- driver.findElement(logout).click();
- }
- }
LoginPage.java: This is the Login page class, in which all the elements of the login page and methods are defined.
- package pages;
- import org.openqa.selenium.By;
- import org.openqa.selenium.WebDriver;
- public class LoginPage {
- WebDriver driver;
- By UserID = By.xpath(“//*[contains(@id,’Login1_UserName’)]”);
- By password = By.xpath(“//*[contains(@id,’Login1_Password’)]”);
- By Submit = By.xpath(“//*[contains(@id,’Login1_LoginButton’)]”);
- //Constructor to initialize object
- public LoginPage(WebDriver driver)
- {
- this.driver = driver;
- }
- public void loginToSite(String Username, String Password)
- {
- this.enterUsername(Username);
- this.enterPasssword(Password);
- this.clickSubmit();
- }
- publicvoid enterUsername(String Username)
- {
- driver.findElement(UserID).sendKeys(Username);
- }
- publicvoid enterPasssword(String Password)
- {
- driver.findElement(password).sendKeys(Password);
- }
- publicvoid clickSubmit()
- {
- driver.findElement(Submit).click();
- }
- }
Step 4: Create Test Cases for the login scenario.
LoginTestCase.java: This is the LoginTestCase class, where the test case is executed. The user can also create more test cases as per the project need.
- package testcases;
- import java.util.concurrent.TimeUnit;
- import library.Browser;
- import library.ScreenShot;
- import org.openqa.selenium.WebDriver;
- import org.testng.Assert;
- import org.testng.ITestResult;
- import org.testng.annotations.AfterMethod;
- import org.testng.annotations.AfterTest;
- import org.testng.annotations.BeforeTest;
- import org.testng.annotations.Test;
- import pages.HomePage;
- import pages.LoginPage;
- public class LoginTestCase {
- WebDriver driver;
- LoginPage lp;
- HomePage hp;
- int i = 0;
- // Launch of the given browser.
- @BeforeTest
- public void browserlaunch()
- {
- driver = Browser.StartBrowser(“Chrome”, “http://demostore.kenticolab.com/Special-Pages/Logon.aspx”);
- driver.manage().timeouts().implicitlyWait(30,TimeUnit.SECONDS);
- lp = new LoginPage(driver);
- hp = new HomePage(driver);
- }
- // Login to the Site.
- @Test(priority = 1)
- public void Login()
- {
- lp.loginToSite(“gaurav.3n@gmail.com”,”Test@123″);
- }
- // Verifing the Home Page.
- @Test(priority = 2)
- public void HomePageVerify()
- {
- String HomeText = hp.pageverify();
- Assert.assertEquals(HomeText, “Logged on as”);
- }
- // Logout the site.
- @Test(priority = 3)
- public void Logout()
- {
- hp.logout();
- }
- // Taking Screen shot on test fail
- @AfterMethod
- public void screenshot(ITestResult result)
- {
- i = i+1;
- String name = “ScreenShot”;
- String x = name+String.valueOf(i);
- if(ITestResult.FAILURE == result.getStatus())
- {
- ScreenShot.captureScreenShot(driver, x);
- }
- }
- @AfterTest
- public void closeBrowser()
- {
- driver.close();
- }
- }
Step 5: Execute “ LoginTestCase.java “.
Step 6: Output of the Page Object Model:
- Launch the Chrome browser.
- The demo website is opened in the browser.
- Login to the demo site.
- Verify the home page.
- Logout the site.
- Close the browser.
What is Page Factory?
Page Factory is an inbuilt Page Object Model concept for Selenium WebDriver but it is very optimized.
Here as well, we follow the concept of separation of Page Object Repository and Test Methods. Additionally, with the help of PageFactory class, we use annotations @FindBy to find WebElement. We use initElements method to initialize web elements
@FindBy can accept tagName, partialLinkText, name, linkText, id, css, className, xpath as attributes.
Let’s look at the same example as above using Page Factory
AjaxElementLocatorFactory
One of the key advantages of using Page Factory pattern is AjaxElementLocatorFactory Class.
It is working on a lazy loading concept, i.e. a timeout for a WebElement will be assigned to the Object page class with the help of AjaxElementLocatorFactory .
Here, when an operation is performed on an element the wait for its visibility starts from that moment only. If the element is not found in the given time interval, Test Case execution will throw a ‘NoSuchElementException’ exception.
- Page Object Model is an Object Repository design pattern in Selenium WebDriver.
- POM creates our testing code maintainable, reusable.
- Page Factory is an optimized way to create object repositories in the POM concept.
- AjaxElementLocatorFactory is a lazy load concept in Page Factory pattern to identify WebElements only when they are used in any operation.
Page Object Pattern using PageFactory
Creating Selenium test cases can result in an un-maintainable project. One of the reasons is that too many duplicated code is used. Duplicated code could be caused by duplicated functionality and this will result in duplicated usage of locators. The disadvantage of duplicated code is that the project is less maintainable. If some locators change, you have to walk through the whole test code to adjust locators where necessary. By using the Page Object Pattern using PageFactory we can make non-brittle test code and reduce or eliminate duplicate test code. Besides that it improves the readability and allows us to create interactive documentation. Last but not least, we can create tests with less keystroke. An implementation of the page object model can be achieved by separating the abstraction of the test object and the test scripts.
How to implement the Page Object Model?
There are two different ways of implementing POM:
- Regular Java classes.
- Page Factory class.
Selenium PageFactory
The PageFactory Class in Selenium is an extension to the Page Object design pattern. It is used to initialize the elements of the Page Object or instantiate the Page Objects itself. Annotations for elements can also be created (and recommended) as the describing properties may not always be descriptive enough to tell one object from the other.
It is used to initialize elements of a Page class without having to use ‘FindElement’ or ‘FindElements’. Annotations can be used to supply descriptive names of target objects to improve code readability. There is however a few differences between C# and Java implementation – Java provides greater flexibility with PageFactory.
How to do it…
We will follow a step by step instruction to set up Page Factory.
Step 1: Take an example of a simple test case of LogIn application and understand where we can use Page factory.
Step 2: Implement Page Factory in the LogIn test case
Step 3: Use Annotations of Page Factory
Step 4: Divide the single Page Factory Objects in to different Page Classes
Step 5: Combine a set of repetitive actions into methods to implement Modularization.
Step 1: Test Case of LogIn application
- Launch a WebDriver
- Navigate to Url www.store.demoqa.com
- Click on My Account link
- Enter Username & Password
- Print a system message and Log out
- Close WebDriver
Step 2: Implement PageFactory in the LogIn test case
The PageFactory relies on using sensible defaults, means the name of the field in the Java class is assumed to be the “id” or “name” of the element on the HTML page.
Let’s see how it works in real world:
- Create a ‘New Package’ file and name it as ‘automationFramework’, by right click on the Project and select New > Package.
- Create a ‘New Class’ file and refer to it as ‘TestCase_POF‘, by right click on the above created Package and select New > Class.
- From the above test ‘LogIn_TestCase’, take the login functionality and implement PageFactory design.
POM Without Pagefactory:
1) Below is how you locate the search field using the usual POM:
- WebElement searchNSETxt=driver.findElement(By.id(“searchBox”));
2) The below step passes the value “investment” into the Search NSE field.
- searchNSETxt.sendkeys(“investment”);
POM Using Pagefactory:
1) You can locate the search field using Pagefactory as shown below.
The annotation @FindBy is used in Pagefactory to identify an element while POM without Pagefactory uses the driver.findElement() method to locate an element.
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 (searchNSETxt in this example).
We will look at the @FindBy annotations in detail in the upcoming part of this tutorial.
@FindBy(id = “searchBox”)
WebElement searchNSETxt;
2) The below step passes the value “investment” into the Search NSE field and the syntax remains the same as that of the usual POM (POM without Pagefactory).
searchNSETxt.sendkeys(“investment”);