logo logo

Test Automation Framework – Page Object Model Example

main post image

In this article, let us look at an example Page Object Model (POM) for a Test Automation Framework. POM is a popular design pattern with classes that represent each page of an application. In some cases, it represents a repeating element such as a menu, header, footer, or widget. By the end of this article, you will read about TestProject’s agent and development token.

  • The agent installs Selenium drivers.
  • The development token is used by the open-source OpenSDK for communication between the agent and TestProject’s platform.

In addition to the agent and development token, we are going to model our first POM, build a CSS Strategy using Chrome Developer Tools, create a Test Class, then view an example report in TestProject. The code will be placed on GitHub at https://github.com/RexJonesII/TestProject-POM-CI_CD.git

Table of Contents

How To Install Selenium Drivers via TestProject’s Agent

The agent from TestProject is a free wrapper that comes with Selenium and Appium dependencies. As a result, we have benefits for testing web and mobile applications. We can install the agent on our local machine, virtual machine, or Docker container. The operating systems include Windows, Mac, and Linux. While installing our agent, the drivers are automatically consolidated for each browser.

Here are the steps for installing TestProject’s agent:

  1. Log into TestProject (or sign up to create a new account if you don’t have one yet. Don’t worry, it’s 100% free)
  2. Click the Agents menu
  3. Select the Download Agent button
    • Available for Windows, Mac, and Linux
    • Available for Docker

Manage Agents in TestProject

TestProject Agent Download

We can also hover over the Agents menu and select Download An Agent. After installing the agent, we must make sure it is running to register the agent. We register by hovering over the Agents menu then selecting Register An Agent. In the background, our agent provides a way to control our devices while maintaining a way to share our Test Scripts.

Select OS for TestProject Agent Download

How To Create Development Token

The purpose of a development token is to help TestProject’s OpenSDK communicate with the agent and platform. We configure the token by defining it as an environment variable or adding it as a parameter in the driver’s constructor.

  • The environment variable is TP_DEV_TOKEN.
  • Example constructor syntax is ChromeDriver driver = new ChromeDriver (“token”);

Here are the steps for creating a developer token:

  1. Log into TestProject
  2. Click the Integrations menu
  3. Click Display token to receive your token

Note: A new token can be created after clicking the circular icon

Get developer token

From the screenshot, we see TestProject’s OpenSDK is available for Java, C#, and Python. It’s 1 solution for our end-to-end Test Automation Scripts.

  • Step 2 allows us to select the programming language.
  • Step 3 installs the OpenSDK.
  • Step 4 provides examples and documentation for the OpenSDK.

Model Your First Page Object Model (POM)

TestProject supports POM in our coded test and when using the AI-Powered Test Recorder. The POM is an efficient approach for managing our Test Scripts. There are 3 components in a POM whereby the Test Scripts are stored separately from the Page Objects:

  1. Web Pages are pages in our Application Under Test (AUT).
  2. Page Objects are class files representing a web page or repeating elements on a web page.
  3. Test Scripts are logical automation steps for testing an AUT.

Web Pages

In this POM model, there will be two Web Pages from TestProject’s Example Page: Login page and Form page. The Login page contains 3 elements and the Form page contains 6 elements for our POM.

  • Login page
    1. Full Name field
    2. Password field
    3. Login button
  • Form Page
    1. Country dropdown
    2. Address field
    3. Email field
    4. Phone field
    5. Save button
    6. Confirmation message

Login page
Form page

Page Objects

BasePage

For a Page Object, it’s optional to include a BasePage. However, the objective of a BasePage is to operate as a superclass with common actions that will be used across other Page Objects. In our model, 2 common actions are typing text into a field and clicking a button. The following BasePage class has a method for type and click.

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public class BasePage {

  protected WebDriver driver;

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

  protected WebElement find (By locator) {
    return driver.findElement(locator);
  }

  protected void type (String text, By locator) {
    find(locator).sendKeys(text);
  }

  protected void click (By locator) {
    find(locator).click();
  }
}

In addition to the common actions, our BasePage contains a constructor and find method. Here’s a breakdown of the BasePage:

  • public BasePage() is the constructor designed to accept a WebDriver driver parameter. The driver will access our browser and locate elements.
  • protected WebElement find () improves readability and reduces code duplication by not writing driver.findElement() in the common method actions. For example, the type and click methods implement find() rather than writing findElement() when locating an element. The By locator parameter accepts a Selenium locator such as id, XPath, and cssSelector.
  • protected void type () enters data into a field using the sendKeys method. The String text parameter receives data while By locator receives a Selenium locator. find(locator) locates an element such as the text field.
  • protected void click () clicks a button on the Login and Form pages. When applicable, this method can also be used to click a link, radio button, or checkbox. The By locator parameter receives a Selenium locator while find(locator) locates the Login and Save buttons.

LoginPage

The LoginPage has 3 variables, 1 constructor, and 3 methods for logging into TestProject’s Example Page. Here’s the code snippet displaying LoginPage extending BasePage so LoginPage can leverage the type and click methods.

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LoginPage extends BasePage {

  private By fullNameField = By.id("name");
  private By passwordField = By.id("password");
  private By loginButton = By.id("login");

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

  public void setFullName (String fullName) {
    type(fullName, fullNameField);
  }

  public void setPassword (String password) {
    type(password, passwordField);
  }

  public FormPage clickLoginButton () {
    click(loginButton);
    return new FormPage(driver);
  }
}
  • Variables
    1. private By fullNameField
    2. private By passwordField
    3. private By loginButton
  • Constructor = public LoginPage (WebDriver driver)
  • Methods
    1. public void setFullName (String fullName)
    2. public void setPassword (String password)
    3. public FormPage clickLoginButton ()

The variables are private so only methods in the LoginPage have access to them. By is a class for locating each element. The constructor calls super(driver) so it can load the constructor from our BasePage. When it comes to methods, this LoginPage class has 2 types of methods:

  1. Setter method helps create a test to interact with our application.
  2. Transition method helps our Test Script adapt when a page changes in the application.

The setter methods are setFullName() and setPassword(). Notice how both methods take advantage of type() from our BasePage.

  • type(fullName, fullNameField) = only write type then fullName followed by the fullNameField variable
  • type(password, passwordField) = only write type then password followed by the passwordField variable

The transition method is clickLoginButton(). It’s called a transition method because the application transitions from the Login Page to the Form Page after clicking the Login button. Our return type is FormPage followed by the method name clickLoginButton. The return type and return new FormPage(driver) complete the transition method. Our BasePage is called again via click then we pass in the loginButton variable.

FormPage

The FormPage has 6 variables, 1 constructor, and 3 methods. Here’s the code snippet for selecting a country, entering data, and clicking the Save button on TestProject’s Example Page.

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.Select;

public class FormPage extends BasePage {

  private By countryDropDown = By.id("country");
  private By addressField = By.cssSelector("#address");
  private By emailAddressField = By.cssSelector("#email");
  private By phoneNumberField = By.xpath("//input[@id='phone']");
  private By saveButton = By.xpath("//button[@id='save']");
  private By confirmationMessage = By.cssSelector("#saved span");

  public FormPage (WebDriver driver) {
    super(driver);
  }

  public void selectFromCountryDropDown (String value) {
    Select country = new Select(find(countryDropDown));
    country.selectByVisibleText(value);
  }

  public void completeForm (String country, String address, String email, String phone) {
    selectFromCountryDropDown(country);
    type(address, addressField);
    type(email, emailAddressField);
    type(phone, phoneNumberField);
    click(saveButton);
  }

  public String getConfirmationMessage () {
    return find(confirmationMessage).getText();
  }
}

The same concept exists for our variables and constructor regarding both Page Objects. However, there are 2 different types of methods. It has a Convenience method and Getter method.

  1. Convenience method combines multiple methods.
  2. Getter method gets the state of our application.

The convenience method is completeForm() and it accepts several parameters with multiple methods. Parameters are country, address, email, and phone while the methods are selectFromCountryDropDown, type, and click. First method selectFromCountryDropDown() was created in this FormPage class but type and click leverage the BasePage.

The getter method is getConfirmationMessage() which gets a message after saving the form. It finds the confirmation message and returns the text via Selenium’s getText method. Here’s a screenshot of the confirmation message labeled “Saved”.

Saved form

How To Build CSS Strategy Using Chrome Developer Tools

Chrome Developer Tools is a set of tools built into Chrome and Edge. Both browsers provide ways to gain more knowledge about a web application. It has several tabs such as Elements, Console, Sources, Network, Performance, Memory, Application, Security, and Lighthouse. In the Elements tab, we can inspect an element by finding a string, selector, or XPath. Here are a screenshot and code snippet of the Login page inspecting the Full Name field.

Element locator strategy

<input id="name" type="text" class="form-control" placeholder="Enter your full name" required="">

It’s best to find an element when the DOM has an id attribute with a unique value. In this example, the id attribute has a value of name. As a result, we can quickly find the element using CSS Selector by writing #name in the Find by string, selector, or XPath text field.

Inspect using Developer Tools

Notice how the input tag is highlighted yellow after writing #name in the text field. That means the Full Name element was successfully located in the DOM. However, in the Selenium automation code, we have the option of choosing different locators.

Recall the LoginPage class used an id locator for the fullNameField, passwordField, and loginButton variables. It’s not a requirement but many engineers start with the Selenium id locator if the id is available in the DOM.

Elements located by ID

Like the Full Name field, most elements (Country, Address, Email, Phone, Save) contain an id attribute on the Form page. Therefore, we can use the Selenium id locator for each variable. However, the FormPage class uses a combination of id, cssSelector, and xpath as the Selenium locators.

❗ Note: We probably would not use a combination of locators if our preferred locators were applicable for finding an element.

Let’s look at the cssSelector and XPath values. The following is a screenshot of Address located using CSS Selector and variables from our FormPage class.

Css Selector in Developer Tools

Using different locator strategies

The addressField and emailAddressField use cssSelector with a value starting with #.

  • addressField = By.cssSelector(“#address”);
  • emailAddressField = By.cssSelector(“#email”)

It’s different for the Phone field and Save button. They use XPath as the Selenium locator. We are going to use the Save button as an example.

XPath example

In the AUT and Page Object class, the XPath value for the Save button is //button[@id=’save’].

  • button is the tag name
  • @id identifies the attribute name
  • ‘save’ shows the value for id is save

The confirmationMessage is different from all other variables in our Page Objects. It does not have an id value and must use cssSelector or XPath. For this example, the cssSelector is shorter than XPath so our FormPage class assigns the variable to #saved span. The value for XPath is //div[@id=’saved’]/h3/span.

Css example

Css example

Create Test Class Using POM and TestProject’s OpenSDK

The Test Classes consist of a class for our BaseTest and a class for executing our automation steps.

BaseTest

The purpose of our BaseTest is similar to a BasePage. It serves as a parent class that will be extended by child classes. The common methods across each Test Class are set up and tear down our test. Set up methods are annotated with @BeforeClass while tear down methods are annotated with @AfterClass. We use a Test Framework like JUnit or TestNG for testing java classes. Here’s the code snippet for BaseTest.

package tests;

import io.github.bonigarcia.wdm.WebDriverManager;
import io.testproject.sdk.drivers.web.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import pages.LoginPage;

public class BaseTest {
  protected ChromeDriver driver;
  private final String URL = "https://example.testproject.io/web/index.html";
  protected LoginPage loginPage;

  @BeforeClass
  public void setUp () throws Exception {
    WebDriverManager.chromedriver().setup();

    driver = new ChromeDriver(
            "add token value from TestProject",
            new ChromeOptions(),
            "My first Project");
    driver.manage().window().maximize();
    driver.get(URL);
    loginPage = new LoginPage(driver);
  }

  @AfterClass
  public void tearDown () throws Exception {
    driver.quit();
  }
}

TestProject’s OpenSDK looks just like pure Selenium code. That’s the benefit of using TestProject. There’s not a learning curve if you are already familiar with Selenium commands. The ChromeDriver import statement is import io.testproject.sdk.drivers.web.ChromeDriver which initiates a new session with the Agent using a provided token and project name.

Initialize ChromeDriver using TestProject

Within the setup() method, we add the token value from TestProject, new ChromeOptions(), then include a project name. The application is loaded by writing driver.get(URL) and we quit the browser via teardown() method.

FormTest

The FormTest class contains easy to read steps for completing the form. They are easy to read because our Page Objects and BaseTest incorporate most of the work. Page Object classes utilize Object-Oriented Programming (OOP) principles and incorporate Selenium WebDriver methods. The BaseTest makes sure our Pre-Conditions and Post-Conditions are in place. Here’s the code snippet for the FormTest class.

package tests;

import org.testng.Assert;
import org.testng.annotations.Test;
import pages.FormPage;

public class FormTest extends BaseTest {

  @Test
  public void logIntoTestProjectExamplePage () {

    loginPage.setFullName("Rex Jones II");
    loginPage.setPassword("12345");
    FormPage formPage = loginPage.clickLoginButton();

    formPage.completeForm(
            "United States",
            "1234 TestProject",
            "[email protected]",
            "214-225-1234");

    Assert.assertTrue(formPage.getConfirmationMessage().equals("Saved"));
  }
}

Before writing our automation steps, we begin by extending our BaseTest. Next, is the @Test annotation which identifies our logIntoTestProjectExamplePage method as a Test Method. At this point, our logic starts on the Login page.

  • Login Page
    • The first step is to enter the Full Name via setFullName(“Rex Jones II”)
    • The second step is to enter the Password via setPassword(“12345”)
    • The final step is to click the Login button via clickLoginButton()
    • Our Test Script transitions to the FormPage
  • Form Page
    • Complete the form by selecting “United States” as the country
    • Enter the address as “1234 TestProject
    • Enter the email as [email protected]
    • Enter the phone as “214-225-1234
  • Verify the form with an assertion. We assertTrue the confirmation message equals Saved on the Form page.

View Example Report In TestProject

The reports are generated automatically and offer visibility into our testing progress. Examples of the Velocity and Individual Run Reports are below after executing the FormTest.

TestProject Velocity report example

Test run report example

The Velocity Report shows a total of 4 executions. 1 test failed and 3 tests passed for a 75% Success Ratio. It also shows the last execution Passed status.

The Individual Run Report provides details for each test and each step. We see 100% Passed for the test and every step on the right side of the report. A green vertical bar indicates that a specific step passed. In addition, we can also download a Summary and/or Full Report of the test. This helps a lot for debugging and understanding the quality of our Application Under Test. 😊

Conclusions

This Page Object Model example should give you an idea of how to build your own automation testing framework using TestProject, Selenium WebDriver and TestNG.

Happy Testing! 🚀

Rex Jones II

About the author

Rex Jones II

Rex Jones II has a passion for sharing knowledge about testing software. His background is development but enjoys testing applications.

Rex is an author, trainer, consultant, and former Board of Director for User Group: Dallas / Fort Worth Mercury User Group (DFWMUG) and member of User Group: Dallas / Fort Worth Quality Assurance Association (DFWQAA). In addition, he is a Certified Software Tester Engineer (CSTE) and has a Test Management Approach (TMap) certification.

Recently, Rex created a social network that demonstrate automation videos. In addition to the social network, he has written 6 Programming / Automation books covering VBScript the programming language for QTP/UFT, Java, Selenium WebDriver, and TestNG.

✔️ YouTube https://www.youtube.com/c/RexJonesII/videos
✔️ Facebook http://facebook.com/JonesRexII
✔️ Twitter https://twitter.com/RexJonesII
✔️ GitHub https://github.com/RexJonesII/Free-Videos
✔️ LinkedIn https://www.linkedin.com/in/rexjones34/

Join TestProject Community

Get full access to the world's first cloud-based, open source friendly testing community. Enjoy TestProject's end-to-end test automation Platform, Forum, Blog and Docs - All for FREE.

Join Us Now  

Leave a Reply

popup image

Selenium for Teams

Sharing and distributing Selenium tests has never been so easy! With TestProject's FREE Selenium based platform, you can finally create awesome tests with the freedom to collaborate with your team effortlessly.
Sign Up Now right arrow
FacebookLinkedInTwitterEmail