Page Object Model with JAVA

Page Object Model is one of the most used test automation framework pattern and in this tutorial I will explain you this test automation design pattern. Before, Ege Aksöz also wrote an article on POM with C#.  You can also see the definition of POM and the main advantages. Thus, I do not go into fundamentals again. I just want to show you a real-life basic POM example with JAVA.

Our POM framework generally consists of 3 basic abstraction layer. These are tests, pages, and util. Complicated frameworks comprise more abstraction layers. We start from the basics so we will have three abstraction layers. In this article, I will not write any class in Util layer but we will improve our POM framework in later articles and I will add new utility classes to this layer.

Tests: It comprises of our test classes.

Pages: It comprises of page classes.

Util: It comprises of utility and helper classes.

I will select one of the most popular Turkish e-commerce site n11.com as a test website and we will implement basic failed login scenarios with POM & JAVA & TestNG. Let’s Start!

At the end of the project, our project structure will be like this.

It is always better to start with requirements, features, or in other terms our tests. It is like a top-down approach and this approach helps you not to make too many mistakes during creating your test automation framework. Thus, first I will start to create our LoginTest class. Our login scenarios are as follows:

  • InvalidUsernameInvalidPassword()
    • Given I am at Home Page
    • When I go to Login Page
    • And I try to login the website with invalid username and password
    • Then I see valid error messages
  • EmptyUsernameEmptPassword()
    • Given I am at Home Page
    • When I go to Login Page
    • And I try to login the website with empty username and password
    • Then I see valid error messages

In test classes:

  1. We need to instantiate the required Page classes
  2. Use the appropriate page methods
  3. Do the assertions

Here is the loginTests.java class:

package tests;

import org.testng.annotations.Test;
import pages.HomePage;
import pages.LoginPage;

public class LoginTests extends BaseTest {
    @Test (priority = 0)
    public void invalidLoginTest_InvalidUserNameInvalidPassword () {

        //*************PAGE INSTANTIATIONS*************
        HomePage homePage = new HomePage(driver);

        //*************PAGE METHODS********************
        homePage.goToN11()
                .goToLoginPage()
                .loginToN11("[email protected]", "11223344")
                .verifyLoginPassword(("E-posta adresiniz veya şifreniz hatalı"))
                .verifyLoginPassword(("E-posta adresiniz veya şifreniz hatalı"));
    }

    @Test (priority = 1)
    public void invalidLoginTest_EmptyUserEmptyPassword () {
        //*************PAGE INSTANTIATIONS*************
        HomePage homePage = new HomePage(driver);

        //*************PAGE METHODS********************
        homePage.goToN11()
                .goToLoginPage()
                .loginToN11("","")
                .verifyLoginUserName("Lütfen e-posta adresinizi girin.")
                .verifyLoginPassword("Bu alanın doldurulması zorunludur.");
    }
}

We have a lot of reds line in this class, we need to start to resolve them until our project will not have any red lines and problems. Let’s start with the BaseTest class.

BaseTest class contains all common functionalities and variables of test classes and all test classes extend this BaseTest class. This is one of the main features of Object Oriented Design (OOD) “Inheritance“. The code of BaseTest is shown below.

package tests;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;

public class BaseTest {
    public WebDriver driver;

    @BeforeClass
    public void setup () {
        //Create a Chrome driver. All test classes use this.
        driver = new ChromeDriver();

        //Maximize Window
        driver.manage().window().maximize();
    }

    @AfterClass
    public void teardown () {
        driver.quit();
    }
}

In this class, we declared the driver variable. This is used by all test classes. Also, we wrote @BeforeClass setup method and @AfterClass teardown method. Again, all test classes use these methods.

In the setup method, we create a ChromeDriver and maximize the browser.

In the teardown method, I closed all the browsers with driver.quit(); code line.

That’s all for the base test class.

Now, let’s write the BasePage class. BasePage class contains the common methods of all page classes such as click, writeText, readText, assertEquals etc. Here is it’s code.

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;

public class BasePage {
    public WebDriver driver;
    public WebDriverWait wait;

    //Constructor
    public BasePage (WebDriver driver){
        this.driver = driver;
        wait = new WebDriverWait(driver,15);
    }

    //Wait Wrapper Method
    public void waitVisibility(By elementBy) {
        wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(elementBy));
    }

    //Click Method
    public void click (By elementBy) {
        waitVisibility(elementBy);
        driver.findElement(elementBy).click();
    }

    //Write Text
    public void writeText (By elementBy, String text) {
        waitVisibility(elementBy);
        driver.findElement(elementBy).sendKeys(text);
    }

    //Read Text
    public String readText (By elementBy) {
        waitVisibility(elementBy);
        return driver.findElement(elementBy).getText();
    }

    //Assert
    public void assertEquals (By elementBy, String expectedText) {
        waitVisibility(elementBy);
        Assert.assertEquals(readText(elementBy), expectedText);

    }
}

Now, we can create our page classes. The first one is HomePage.java class. In this class we will declare:

  • Constructor 
  • Page Variables
  • Web Elements
  • Page Methods

We will have two methods, one of them opens the homepage and the other one opens the login page. Here is the code.

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import sun.rmi.runtime.Log;

public class HomePage extends BasePage {

    //*********Constructor*********
    public HomePage (WebDriver driver) {
        super(driver);
    }

    //*********Page Variables*********
    String baseURL = "http://www.n11.com/";

    //*********Web Elements*********
    By signInButtonBy = By.className("btnSignIn");


    //*********Page Methods*********
    //Go to Homepage
    public HomePage goToN11 (){
        driver.get(baseURL);
        return this;
    }

    //Go to LoginPage
    public LoginPage goToLoginPage (){
        click(signInButtonBy);
        return new LoginPage(driver);
    }
}

Another page class in LoginPage.java class. We will have three methods. One of them does the login operation, the other ones are assertion methods. Checks the login messages as expected or not and set the test fail or pass. Here is the code of LoginPage.java class.

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;

public class LoginPage extends BasePage{

    //*********Constructor*********
    public LoginPage(WebDriver driver) {
        super(driver);
    }

    //*********Web Elements*********
    By usernameBy = By.id("email");
    By passwordBy = By.id("password");
    By loginButtonBy = By.id("loginButton");
    By errorMessageUsernameBy = By.xpath("//*[@id=\"loginForm\"]/div[1]/div/div");
    By errorMessagePasswordBy = By.xpath("//*[@id=\"loginForm\"]/div[2]/div/div ");

    //*********Page Methods*********

    public LoginPage loginToN11 (String username, String password){
        //Enter Username(Email)
        writeText(usernameBy,username);
        //Enter Password
        writeText(passwordBy, password);
        //Click Login Button
        click(loginButtonBy);
        return this;
    }

    //Verify Username Condition
    public LoginPage verifyLoginUserName (String expectedText) {
        assertEquals(errorMessageUsernameBy, expectedText);
        return this;
    }

    //Verify Password Condition
    public LoginPage verifyLoginPassword (String expectedText) {
        assertEquals(errorMessagePasswordBy, expectedText);
        return this;
    }
}

Here are the pom.xml and TestNG.xml files.

<?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>n11project</groupId>
    <artifactId>n11project</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.14.0</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <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.19</version>
                <configuration>
                    <suiteXmlFiles>
                        <suiteXmlFile>TestNG.xml</suiteXmlFile>
                    </suiteXmlFiles>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite"  >
    <test name="LoginTest">
        <classes>
            <class name="tests.LoginTests"/>
        </classes>
    </test>
</suite>

When we run our project, we will get below test results ;)

We can improve this POM framework with Utility classes such as DesiredcapabilitiesManager, AJAXWaiter, Assertions, Listeners, Reporters, PropertyReaders, and so on. We will see the improved version of this in the next articles. You can reach the source code of the project on swtestacademy POM JAVA Example Github Page.

Selenium Webdriver Tutorial Series

[fusion_widget_area name=”avada-custom-sidebar-seleniumwidget” title_size=”” title_color=”” background_color=”” padding_top=”” padding_right=”” padding_bottom=”” padding_left=”” hide_on_mobile=”small-visibility,medium-visibility,large-visibility” class=”” id=””][/fusion_widget_area]

See you!
-Onur

40 thoughts on “Page Object Model with JAVA”

  1. Great article! In my opinion without technical background, POM is one of the hardest part to understand. Well written. Thanks!
    I’m waiting for the new articles about it

    Reply
  2. Hi,
    I have question about PAGE INSTANTIATIONS,
    Is it possible to create a page Instantiations method and call from each @Test to reach the variables and methods ?
    Thanks

    Reply
  3. Hi Onur,

    I have a question about the parameterized constructor here. In each page class you are creating a page constructor where you are passing the Webdriver driver and Webdriver wait and then calling a super method with driver and wait variables. So, on instantiating in the Login.test class, the control will flow to base test class first or base page class? could you please explain with a pictorial representation.

    Thanks

    Reply
    • Hi Shrikant,

      Yes, you are right. In BaseTest class I created driver and wait and pass these variables to page classes in specific test classes. We started to send these variables from BaseTest class to Page classes. You understood well.

      Reply
  4. Hi,

    I think If we return ‘this’ or ‘new’ instead of ‘void’, we can use method chaining and write more fluent test cases like

    homePage.goToN11().goToLoginPage().loginPage.loginToN11(“”,””);

    Reply
  5. Hi Onur,
    I opened the project just as you said,
    but when when i try to run the test intellij says:
    java.lang.Exception: Method setup() should be static

    java.lang.Exception: Method teardown() should be static

    I understand i can change those methods and driver in basetest to static, but i’m trying to understand why in your code it’s not static,
    Thanks

    Reply
  6. Hi Onur,

    Thank you for explaining POM in simple way. Have you written article on Utility Class? I tried it but didn’t found. Request you to please share the link with me if you have already created and it is available.

    Reply
  7. This is by far the best explanation of POM automation framework. The examples are clear, easy to follow and practical. Amazing how you simplied something so abstract for anyone to follow.

    Thank you

    Reply
  8. I feel one redundancy here

    //*************PAGE INSTANTIATIONS*************
    HomePage homePage = new HomePage(driver,wait);
    LoginPage loginPage = new LoginPage(driver,wait);

    we are doing the page initiation in each test case; can’t we do some better work around so that it should not be declared again and again for each test case.

    Reply
  9. The site (https://www.n11.com/) you are using for the test is not in English. I would like to try out the test but when I connect to this site, I’m completely lost. Diffcult to get the fields and web elements you are using. Could you plse let me know how I can follow up?

    Reply
  10. Thanks for your reply. What I actually did was to use Chrome with the Translation activated for the site. So I was able to see fields corresponding to web element to act on. For example, for the site in English, I was able to make the following correspondences

    Koum Belirie (Location)
    Giris Yap (Login)
    Uye Ol (Sign Up)

    Thank you for the useful tutorial.

    Reply
  11. I think its better to put asserts in the page like you did ..
    and most of the people think that it should be in the test
    what is your opinion about that ?

    Reply
  12. Hi, Onur.

    I find that when switching between test cases in the extent report, it would not jump back to the top automatically. May I ask if there is any way to implement it?

    Thanks,
    Ben

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.