How to Implement POM Design Pattern with Selenium

Hassan Alhejaili
6 min readApr 1, 2024

--

In this article, we’ll discuss the advantages of using the Page Object Model (POM) for automated testing with Selenium, Java, and JUnit 5. First, we’ll explain what POM is and then try writing tests without it. During this process, we’ll note any difficulties we face. After that, we’ll tackle these issues by applying POM principles to our code. This approach will show how POM improves the reusability, readability, and maintenance of tests.

Introduction:

The Page Object Model (POM) is a popular approach used in test automation to improve how we manage code and ensure stable testing. By organizing web page elements and their actions into reusable parts, POM helps us avoid repeating code and keeps our test scripts clear and easy to manage.

Why Use POM?

Enhanced Code Reusability: Page objects can be reused in different tests, which means less repetition and more efficient testing.

Improved Test Readability and Maintainability: POM organizes page details into easy-to-understand components. This makes test scripts shorter, clearer, and easier to manage over time.

Reduced Code Duplication: POM helps to avoid repeating the same code for finding and interacting with web elements. This leads to cleaner, more efficient code.

Simplified Maintenance: When the user interface (UI) changes, updates only need to be made in the page objects. This makes it faster and easier to keep test scripts up-to-date.

Test Cases Overview:

Here are the test cases we’ll be working with:

Test Case LF1: Verify valid login

Test Steps:

  1. Open the OrangeHRM login page.
  2. Enter valid credentials (username: Admin, password: admin123).
  3. Click on the “Login” button.

Expected Results: Redirected to the dashboard page.

Test Case LF2: Verify error message for invalid credentials

Test Steps:

  1. Open the OrangeHRM login page.
  2. Enter invalid credentials (username: root, password: ads123).
  3. Click on the “Login” button.

Expected Results: Error message “Invalid credentials”.

Implementing Without POM:

First, we’ll create a class called TestBase to establish a common setup and teardown for our tests. This class is like the base of our testing setup, making sure everything works the same way every time we run a test.

Next, we’ll create a test class called LoginTests. This class contains two test methods for verifying the login functionality.

Challenges and Issues:

  • Moving the repeated code on lines 6 and 16, to the setUp() method initially appears as a solution but becomes cumbersome if other tests navigate to different pages
  • Creating a navigateToLoginPage() method in LoginTests helps reduce code repetition. However, if other test classes need this method, it breaks the DRY (Don't Repeat Yourself) principle, leading to more work in maintaining the code.

A better solution is to use the Page Object Model (POM) design pattern. This involves creating a method called navigateToLoginPage() in the LoginPage class. This makes it easier to navigate between pages.

Here’s how we can navigate to the login page using POM:

new LoginPage(driver).navigateToLoginPage();j

or

LoginPage loginPage = new LoginPage(driver);
loginPage.navigateToLoginPage();

This approach helps in reducing code duplication and ensures easier maintenance. If there’s a need to change the link, we only need to modify it in ONE PLACE.

Also, these elements are duplicated:

driver.findElement(By.name("username")).sendKeys("Admin");
driver.findElement(By.name("password")).sendKeys("admin123");
driver.findElement(By.tagName("button")).click();

Duplicating these elements increases the maintenance overhead. Any changes to the login process, such as updating element locators, would need to be applied in multiple locations, increasing the risk of errors and making the code harder to maintain.

Imagine if you have multiple test classes that require login functionality. With the current approach of duplicating login-related elements and actions in each test method, any change or update to the login process would necessitate modifying code in multiple places. This not only increases the likelihood of errors but also makes maintenance tasks cumbersome and time-consuming.

Implementing with POM:

If we separate each page along with its elements, methods, and actions into corresponding classes (Page Objects), we can reap several benefits:

Using the Page Object Model (POM) design pattern to structure our automation framework allows us to build more reliable, maintainable, and efficient automated tests.

This approach allows us to encapsulate the functionality and behavior of each page within dedicated classes, improving code organization and enhancing test maintainability. Additionally, it promotes code reuse by providing a centralized location for interacting with page elements, leading to more efficient test development and easier maintenance.

Steps to implement POM:

1- Project Structure Setup: Begin by organizing your project structure. Create a directory named pages within the src/main/java/

2- Page Object Creation: within the pages directory, create a new Java class named LoginPage.java to represent the login page. This class will encapsulate all interactions with the login page elements.

In this step, we create the initial LoginPage class with the necessary fields for locating elements on the login page and add the navigateToLoginPage() method. This method navigates the WebDriver instance to the login page URL.

In this step, we will add methods to the LoginPage class for interacting with the login page elements such as entering the username, password, and clicking the login button.

loginValidUser(): Enters valid username and password, clicks the login button, and returns a DashboardPage object.

loginInvalidUser(): Enters invalid username and password, clicks the login button.

getInvalidMessage(): Retrieves the text of the invalid credentials message.

Let’s refactor login methods to avoid duplication and improve code readability

Since we navigate to another page (Dashboard Page) when the user successfully logs in, we should create a Page Object that represents the Dashboard Page:

Finally, let’s optimize our tests by refactoring the test methods in the LoginTests class to utilize the refactored login methods from the LoginPage page object.

The main idea is to encapsulate and abstract the logic for interacting with web elements, thus separating the test code from implementation details.

In test code, the primary focus should be on defining test cases and making assertions. The details of interacting with web elements and logic are encapsulated within the Page Object.

A while ago I was wondering. Where should we define Assertions? In test code or page object? cause I see some testers write assertions in test code and others write assertions in page objects.

According to the guidelines:

Page objects themselves should never make verifications or assertions. This is part of your test and should always be within the test’s code, never in an page object. The page object will contain the representation of the page, and the services the page provides via methods but no code related to what is being tested should be within the page object.

Therefore, assertions should be included in the test code.

Resources:

Page Object Models: Explore the Selenium documentation for best practices and guidelines on implementing the Page Object Model (POM).

GitHub Repository: Access the base code for the project discussed in this article.

Conclusion:

In conclusion, adopting the Page Object Model (POM) in our test automation framework brings numerous benefits. From enhanced code reusability and readability to simplified maintenance.

--

--