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.

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() {

           ChromeOptions options = new ChromeOptions();
           driver = new ChromeDriver(options);
           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().

ChromeOptions options = new ChromeOptions();
driver = new ChromeDriver(options);
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);
     }

Implementation Steps

Step 1 – Create a Maven Project

Click here to know How to create a Maven project.

Step 2 – Add dependencies to the pom.xml

<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>com.example</groupId>
  <artifactId>PageObjectModel</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>PageObjectModel</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <selenium.version>4.21.0</selenium.version>
    <testng.version>7.10.2</testng.version>
    <maven.surefire.plugin.version>3.2.5</maven.surefire.plugin.version>
    <maven.compiler.plugin.version>3.13.0</maven.compiler.plugin.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>${selenium.version}</version>
    </dependency>

    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>${testng.version}</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven.compiler.plugin.version}</version>
        <configuration>
          <source>${maven.compiler.source}</source>
          <target>${maven.compiler.target}</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${maven.surefire.plugin.version}</version>
        <configuration>
          <suiteXmlFiles>
            <suiteXmlFile>testng.xml</suiteXmlFile>
          </suiteXmlFiles>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

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().

package com.example.pages;

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

public class BasePage {
    public WebDriver driver;

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

}

Login Page

package com.example.pages;

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

public class LoginPage extends BasePage{

    public LoginPage(WebDriver driver) {
        super(driver);

    }

    @FindBy(name = "username")
    public WebElement userName;

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

    @FindBy(xpath = "//*[@class='oxd-form']/div[3]/button")
    public WebElement login;

    @FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/div/div[1]/div[1]/p")
    public  WebElement errorMessage;


    public void login(String strUserName, String strPassword) {

        userName.sendKeys(strUserName);
        password.sendKeys(strPassword);
        login.click();

    }

    public String getErrorMessage() {
        return errorMessage.getText();
    }


}

HomePage. java

package com.example.pages;

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

public class HomePage extends BasePage {

    public HomePage(WebDriver driver) {
        super(driver);

    }

    @FindBy(xpath = "//*[@id='app']/div[1]/div[1]/header/div[1]/div[1]/span/h6")
    public  WebElement homePageUserName;

    public String getHomePageText() {
        return homePageUserName.getText();
    }

}

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

package com.example.tests;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

import java.time.Duration;

public class BaseTests {

    public static WebDriver driver;
    public final static int TIMEOUT = 10;

    @BeforeMethod
    public void setup() {

        ChromeOptions options = new ChromeOptions();
        options.addArguments("--remote-allow-origins=*");
        options.addArguments("--no-sandbox");
        options.addArguments("--disable-dev-shm-usage");
        options.addArguments("--headless");
        driver = new ChromeDriver(options);
        driver.manage().window().maximize();
        driver.get("https://opensource-demo.orangehrmlive.com/");
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));

    }

    @AfterMethod
    public void tearDown() {
        driver.quit();
    }

}

LoginPageTests.java

package com.example.tests;

import com.example.pages.HomePage;
import com.example.pages.LoginPage;
import org.testng.Assert;
import org.testng.annotations.Test;

public class LoginPageTests extends BaseTests{

    String actualResponse;

    @Test
    public void invalidCredentials()  {

        LoginPage objLoginPage = new LoginPage(driver);
        objLoginPage.login("admin", "admin");
        actualResponse = objLoginPage.getErrorMessage();
        Assert.assertEquals(actualResponse,"Invalid credentials");
    }

    @Test
    public void validCredentials() {

        LoginPage objLoginPage = new LoginPage(driver);
        objLoginPage.login("Admin", "admin123");
        HomePage objHomePage = new HomePage(driver);
        actualResponse = objHomePage.getHomePageText();
        Assert.assertEquals(actualResponse,"Dashboard");

    }

}

Step 5 – Execute the tests

To run the test, right click and select as Run As and then select TestNG Test (Eclipse).

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.tests.LoginPageTests"/>
        </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.

Index.html

emailable-report.html

mvn clean test

Maven – How to add M2_REPO classpath variable to Eclipse

New version of Eclipse like Photon or Eclipse IDE 2019-06 has M2_REPO classpath variable after integrating maven plugins (m2eclipse) with Eclipse IDE, M2_REPO classpath variable gets added –> pointing to default locations (for example c:\Users\\.m2\repository) and this variable is non-modifiable. However, if we are using older version of Eclipse, then we need to add M2_REPO manually to Eclipse.

Steps to add M2_REPO

  1. Open Eclipse IDE, select Window ->Preferences ->Java ->Classpath Variables

2. Click on New Button. Add below mentioned information:-

Name – M2_REPO

Path – Path where M2 file places in your system

Eg – C:\Users\SingVi04\.m2\repository

Note:- I have already added M2_REPO, so we can see an error message – Variable name already exists.

3. Verify that M2_REPO add – You can check new Classpath variable M2_REPO is added under BuildPath ->Classpath Variables

Maven – How to import Maven Project into Eclipse

 
 

In the previous tutorial, we have seen how we can create a Java project from Command Line. In this tutorial, will see how we can import this project in Eclipse.

  1. We need to make sure if Eclipse Maven Integration(m2e) is present . Latest Eclipse IDE has bundled the m2e plugin.

2. If you are using older version of Eclipse. Then below are the steps to follow

2.1 Open Eclipse IDE and select Help ->Install New Software

2.2 Click on Add button to add a new Repository

2.3 Fill the below mentioned information in the dialog box:-

Name – M2Eclipse

Location – http://download.eclipse.org/technology/m2e/releases

2.4. After the Pending finish, select all the Plugins and press Next.

2.5. Accept the terms of the license agreement and click Finish

2.6. At the end of the installation, you will be ask to restart your Eclipse. Click Yes to perform the restart.

2.7 To check if the installation is successful, go to Help ->About Eclipse

3. In Eclipse IDE, select File ->Import ->Maven ->Existing Maven Project. 

4. The m2e plugin will analyze the pom.xml and will configure the project and generate the Eclipse files automatically.

5. Below is the code of App.java. Run this code

package com.Selenium;
 
/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
    }
}

6. Below is the code of AppTest.java. Run this code

package com.Selenium;
 
import static org.junit.Assert.assertTrue;
import org.junit.Test;
 
/**
 * Unit test for simple App.
 */
public class AppTest 
{
    /**
     * Rigorous Test 🙂
     */
    @Test
    public void shouldAnswerWithTrue()
    {
        assertTrue( true );
    }
}

Note:- Apache Maven Eclipse Plugins like eclipse:eclipse, eclipse:clean, etc are retired. To know more about it, please refer the link

7. Structure of POM.xml

<?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>com.Selenium</groupId>
  <artifactId>MavenProjectFromCMD</artifactId>
  <version>1.0-SNAPSHOT</version>
 
  <name>MavenProjectFromCMD</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>
 
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>
 
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
 
  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Mavendefaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>

        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>

        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>

        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>

        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>

        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>

        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>

        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>

        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

Maven – How to create a Java project using Command Line

 
 

     In the previous tutorial, we have discussed about How to install Maven on Windows. In this tutorial, we will see how to use Maven to manage a Java project – Create and update the dependencies.

   1) Change current folder to the folder where we want to create the Java project 

   In my case I have my Eclipse Workspace mentioned at the below mentioned path 

    cd C:\Users\vibha\eclipse-workspace\Selenium

  2) Create a Project from Maven Template

This tells Maven to generate a Java project from a Maven template.

mvn archetype:generate

3) We need to mention the number as displayed on your screen in Command Prompt to proceed further. Like here, Choose a folder or apply has 1394, so I have also mentioned 1394 in command prompt.

4) We need to provide again  input in command prompt. This time program wants to know which version we want to use. I prefer to use the latest version. Here, it is 8, so I have selected version 8.

5) We need to provide 2 input here

A) Value of groupId – This serves as the group identifier of your Maven project, it should be in a form similar to Java packages, such as com.Selenium
B) Value of artifactId – This serves as the group-local identifier of my Maven project like MavenProjectFromCMD
C) Value of Version – The initial version of our project. The default is 1.0-SNAPSHOT
D) Value of package – The name of our root package. The default is groupId we have created earlier.
We will notice the INFO message about the properties. If the displayed settings are correct, then just enter Y in :: prompt.

Successful Build – Below screenshot shows that the Maven Project built successfully.

  6) Project Folder Creation – We can see a folder with the name of project – MavenProjectFromCMD in our Eclipse Workspace. In my case, it is                     

    C:\Users\vibha\eclipse-workspace\Selenium\MavenProjectFromCMD

7) Contents of Project Folder – Open folder MavenProjectFromCMD to see the contents of the folder. It should have POM file and src

How to install Maven on Windows

HOME

Step 2 – Unzip the downloaded folder and then it will have below-mentioned files. We do not need to install anything, just unzip the folder.

Step 3 – We need to configure MAVEN_HOME environment variable. Type – “View Adva” in the search option, and we will see the option – View Advanced system setting.

Step 4 – In the System Properties dialog, select the Advanced tab and click on the Environment Variables button.

Step 5 – In the “Environment variables” dialog, under Users variables, Click on the New button and add a MAVEN_HOME variable.

Step 6 – A dialog box will appear, mentioning Variable Name – MAVEN_HOME and Variable value – mention the path where the Apache folder is placed.

Step 7 – Add %MAVEN_HOME%\bin (full path till bin where Maven is placed on your machine) to Path present under System variables. Click the New Button present in System Variable and add MAVEN_HOME\bin.

Step 8 – Once the Path is updated with %MAVEN_HOME%\bin. This is what it will look like.

Step 9 – We have to make sure that JDK is installed and the JAVA_HOME environment variable is configured. If the JAVA_HOME variable is not configured, then add JAVA_HOME just like MAVEN_HOME in User Variable within Environment Variables.

How to verify if Maven is installed properly on your machine

Open the command prompt and type mvn -version, then the screen should look something like as shown below screen

This confirms that Maven is installed successfully and configured on your machine.  

Selenium Introduction

HOME

    WebDriver talks to a browser through a driver. Communication is two-way: WebDriver passes commands to the browser through the driver and receives information back via the same route. The driver is specific to the browser, such as ChromeDriver for Google’s Chrome/Chromium, GeckoDriver for Mozilla’s Firefox, etc. The driver runs on the same system as the browser.