Use Page Objects in Selenium Java to keep the tests maintainable

This tutorial will handle page objects in Selenium Java. It builds on the tutorial Cross-Browser Selenium Java with automated WebDriver Download. Therefore, you should check out the git repository mentioned in the project before starting with this tutorial.

In the Cross-Browser-Test tutorial, the logo of the test-with-a-smile website is checked, by identifying it as Xpath within the test. Keeping the identifiers inside the tests, has several disadvantages. On the one hand, you need to copy the Xpath every time you use the same element within another test. On the other hand the maintenance increases when the element changes. If the Xpath needs to be adjusted, this needs to be done in every test the element is used.
By creating a Page Object, the identifier is written in one single file and can be used by every test. If the Xpath or id of the element changes, it only needs to be adjusted in the Page Object to fix it for every test.

Table of contents

Add a page object to the Selenium Framework
Use the elements of a page object in a test
Add methods to page objects for a small keyword driven approach
Create a page object with recognition by different identifiers
Rewrite the test to match the improved page objects
Download source code
Next step

Add a page object to the Selenium Framework

In this tutorial the project from the Cross-Browser-Testing will be used as based and extended using Visual Studio Code. It is also possible to use other editors like Eclipse or even Notepad++.
All page objects should be stored in one folder. For this tutorial create the folder src\test\java\com\tws\testframework\pageobjects and add the file TwsMain.java. This file will contain all objects handled on the Test With a Smile homepage in the main view. For other views, for example a blog entry, a new page object has to be created. One page object should always match one view on the website that will be tested.

In the new page object first add the imports for WebElement, CacheLookup, FindBy and How. All of them will be used by the page object. After that, the file should look like this:

package com.tws.twsframework.pageobjects;

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;

public class TwsMain {

}

Now all objects that will be used by the tests can be added to the page object. Each elements get’s its own variable and can be identified by different identifiers like id, Xpath, CSS, etc. For this tutorial add the elements Logo, Search input, Search button, Impressum link, Data protection link and About the author link. After that, the page object should look like this:

package com.tws.twsframework.pageobjects;

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;

public class TwsMain {

    @FindBy(how = How.XPATH, using = "//*[@id='headimg']/a/img")
    @CacheLookup
    public WebElement logo;
    
    @FindBy(how = How.XPATH, using = "//*[@id='search-2']/form/label/input")
    @CacheLookup
    public WebElement inp_search;
    
    @FindBy(how = How.XPATH, using = "//*[@id='search-2']/form/button")
    @CacheLookup
    public WebElement btn_search;
    
    @FindBy(how = How.ID, using = "impressum")
    @CacheLookup
    public WebElement impressum;
    
    @FindBy(how = How.ID, using = "dataProtection")
    @CacheLookup
    public WebElement dataProtection;
    
    @FindBy(how = How.ID, using = "about")
    @CacheLookup
    public WebElement aboutTheAuthor;
    
}

In this tutorial we will open the main page, check some elements and perform a search. To validate the search result a second page object for the search result needs to be set up. For this create the file SearchResult.java and add them elements searchResultFor and firstResultHeadline in the page object. The file should look like this:

package com.tws.twsframework.pageobjects;

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;

public class SearchResult {

    @FindBy(how = How.XPATH, using = "//*[@id='main']/header/h1")
    @CacheLookup
    public WebElement searchResultText;
    
    @FindBy(how = How.XPATH, using = "//*[@id='post-35']/div/header/h2/a")
    @CacheLookup
    public WebElement firstResultHeader;
    
}

Use the elements of a page object in a test

After implementing the page objects it’s time to edit the test in FirstTest.java. The test has to perform the following actions:

  1. Open the website
  2. Check if the Logo and all links in the footer are present
  3. Search for the term “Create a Cross-Browser-Test Selenium Java Project with automated WebDriver Download”
  4. Verify the Search Result for term
  5. Verify the headline of the first search result

After adding the steps to the test, the file FirstTest.java should look like this:

package com.tws.twsframework.tests;

import com.tws.twsframework.framework.Browser;
import com.tws.twsframework.framework.BrowserProvider;

import com.tws.twsframework.pageobjects.*;

import org.openqa.selenium.support.PageFactory;

import org.openqa.selenium.WebDriver;

import org.testng.Assert;
import org.testng.annotations.Test;
import org.testng.annotations.AfterMethod;

public class FirstTest{

    public Browser browser;

    @Test(dataProvider = "browser-data-provider", dataProviderClass = BrowserProvider.class)
    private void testCase(String browsername) throws Exception{
        browser = new Browser(browsername, 10);
        browser.driver.get("https://test-with-a-smile.de");
        
        TwsMain poTwsMain = PageFactory.initElements(browser.driver, TwsMain.class);

        Assert.assertEquals(true, poTwsMain.logo.isDisplayed());
        Assert.assertEquals("Impressum", poTwsMain.impressum.getText());
        Assert.assertEquals("Data protection", poTwsMain.dataProtection.getText());
        Assert.assertEquals("About the author", poTwsMain.aboutTheAuthor.getText());

        poTwsMain.inp_search.sendKeys("Create a Cross-Browser-Test Selenium Java Project with automated WebDriver Download");
        poTwsMain.btn_search.click();

        SearchResult poSearchResult = PageFactory.initElements(browser.driver, SearchResult.class);
        
        Assert.assertEquals("Search Results for: Create a Cross-Browser-Test Selenium Java Project with automated WebDriver Download", poSearchResult.searchResultText.getText());
        Assert.assertEquals("Create a Cross-Browser-Test Selenium Java Project with automated WebDriver Download", poSearchResult.firstResultHeader.getText());
        
    }
    
    @AfterMethod
    private void closeBrowsers(){
        browser.driver.quit();
    }
}

Add methods to page objects for a small keyword driven approach

If you want to check the footer or perform a search in multiple tests you always have to write several lines to approach this. That’s where methods in page objects can be useful and bring you close to a keyword driven approach. A keyword driven approach means, that you only call methods in the tests. These methods can contain elements from several pages. In this approach only elements from the given page can be used.

The methods in the page objects can be called in the tests and perform different actions at once, for example checking the three footer elements or search for something. This reduces the code in the text and makes refactoring simpler. A new link in the footer for example only needs to be added in the method of the page object and this way will be checked in every test that calls the method. With the methods for the footer and the search, the TwsMain.java will look like this:

package com.tws.twsframework.pageobjects;

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;

import org.testng.Assert;

public class TwsMain {

    @FindBy(how = How.XPATH, using = "//*[@id='headimg']/a/img")
    @CacheLookup
    public WebElement logo;
    
    @FindBy(how = How.XPATH, using = "//*[@id='search-2']/form/label/input")
    @CacheLookup
    public WebElement inp_search;
    
    @FindBy(how = How.XPATH, using = "//*[@id='search-2']/form/button")
    @CacheLookup
    public WebElement btn_search;
    
    @FindBy(how = How.ID, using = "impressum")
    @CacheLookup
    public WebElement impressum;
    
    @FindBy(how = How.ID, using = "dataProtection")
    @CacheLookup
    public WebElement dataProtection;
    
    @FindBy(how = How.ID, using = "about")
    @CacheLookup
    public WebElement aboutTheAuthor;

    public void checkFooter(){
        Assert.assertEquals("Impressum", impressum.getText());
        Assert.assertEquals("Data protection", dataProtection.getText());
        Assert.assertEquals("About the author", aboutTheAuthor.getText());
    }

    public void searchFor(String searchTerm){
        inp_search.sendKeys(searchTerm);
        btn_search.click();
    }
    
}

After adding the new methods the test in FirstTest.java can be simplified by replacing the single calls with method calls. The file FirstTest.java should look like this:

package com.tws.twsframework.tests;

import com.tws.twsframework.framework.Browser;
import com.tws.twsframework.framework.BrowserProvider;

import com.tws.twsframework.pageobjects.*;

import org.openqa.selenium.support.PageFactory;

import org.openqa.selenium.WebDriver;

import org.testng.Assert;
import org.testng.annotations.Test;
import org.testng.annotations.AfterMethod;

public class FirstTest{

    public Browser browser;

    @Test(dataProvider = "browser-data-provider", dataProviderClass = BrowserProvider.class)
    private void testCase(String browsername) throws Exception{
        browser = new Browser(browsername, 10);
        browser.driver.get("https://test-with-a-smile.de");
        
        TwsMain poTwsMain = PageFactory.initElements(browser.driver, TwsMain.class);

        Assert.assertEquals(true, poTwsMain.logo.isDisplayed());
        poTwsMain.checkFooter();

        poTwsMain.searchFor("Create a Cross-Browser-Test Selenium Java Project with automated WebDriver Download");

        SearchResult poSearchResult = PageFactory.initElements(browser.driver, SearchResult.class);
        
        Assert.assertEquals("Search Results for: Create a Cross-Browser-Test Selenium Java Project with automated WebDriver Download", poSearchResult.searchResultText.getText());
        Assert.assertEquals("Create a Cross-Browser-Test Selenium Java Project with automated WebDriver Download", poSearchResult.firstResultHeader.getText());
        
    }
    
    @AfterMethod
    private void closeBrowsers(){
        browser.driver.quit();
    }
}

Download source code

You can find the fully commented source code at GitHub:
https://github.com/TestautomationPopp/Page-Objects-for-Selenium-Java

Next step

To improve this framework the next step is to implement a data driven approach.