Importance of POM in Test Automation Framework with Python (UnitTest)Example

Bisma Latif
4 min readDec 23, 2022

--

Page Object Model (POM) is a design pattern commonly used in test automation to represent the UI elements of a web page as a class. It is an effective way to organize and maintain test scripts because it separates the test logic from the UI elements of the application under test. This separation of concerns makes it easier to modify the UI elements without affecting the test logic, and vice versa.

There are several benefits to using the Page Object Model in a test automation framework:

  1. Reusability: UI elements represented as page objects can be reused across multiple tests, reducing the need to write duplicate code and making it easier to maintain the test suite.
  2. Maintainability: Since the test logic is separated from the UI elements, it is easier to modify the UI elements without affecting the test logic. This makes it easier to maintain the test suite as the application under test evolves over time.
  3. Readability: Page Object Model makes it easier to understand the test scripts because the test logic is separated from the UI elements. This makes it easier for new team members to understand the test suite and for the maintenance of the test suite.
  4. Modularity: The Page Object Model promotes modularity because it allows tests to be grouped into logical units, making it easier to understand the comprehensive test suite and to make changes to the test suite when necessary.

Here is an example of how the Page Object Model can be implemented in a test automation framework using Python and the Selenium WebDriver library:

This is a single test.py file containing the driver, elements, and test cases:

# import the necessary libraries
from selenium import webdriver

# define the Page class
class Page:
def __init__(self, driver):
self.driver = driver
self.url = "http://www.example.com"

def open(self):
self.driver.get(self.url)

# define the LoginPage class
class LoginPage(Page):
def __init__(self, driver):

self.username_input = driver.find_element_by_id("username")
self.password_input = driver.find_element_by_id("password")
self.submit_button = driver.find_element_by_id("submit")

def login(self, username, password):
self.username_input.send_keys(username)
self.password_input.send_keys(password)
self.submit_button.click()

# create an instance of the webdriver
driver = webdriver.Chrome()

# create an instance of the LoginPage
login_page = LoginPage(driver)

# open the login page
login_page.open()

# login to the application
login_page.login("username", "password")

# close the webdriver
driver.quit()

In this example, the Page class represents a generic web page and defines the URL and an open() method to navigate to the page. The LoginPage class extends the Page class and defines UI elements specific to the login page, as well as a login() method to enter the username and password and submit the form.

To port this implementation to the Page Object Model in Selenium & Python, we use a directory structure that can be applied regardless of project size and complexity. The directory structure chosen is based on test requirements, test complexity, and past development and testing team experience.

Project-Directory
|--------- Src
|--------- PageObject
|--------- Pages
|--------- *Page.py (Implementation of methods that make use of the respective Locators declared in Locators.py)
|--------- Locators.py
|--------- TestBase
|--------- WebDriverSetup.py
|--------- Test
|--------- Scripts
|--------- test_*.py (Implementation of test code)(There should be 1:1 mapping of *Page.py and test_*.py as it helps in making the code more modular)
|--------- TestSuite
|--------- TestRunner.py (contains TestSuite, which is a collection of test cases)
Image by saucelabs.com

Create a Locators File:

Based on the above structure, the locators will be moved to the locators.py file. The structure of the locators file will be:

Locators
— — — locators.py

class Locator(object):

username_input = 'username'
password_input = 'password'
submit_button = 'submit'

Create a Base File:

Create a Base.py file, and keep it in under the Pages folder, having a hierarchy like:

Pages
— — — Base.py

— — — Login.py

Image by https://anoopjshetty.wordpress.com/
from selenium.webdriver.common.by import By
#This is the base page which defines attributes and methods that all other pages will share

class BasePage:
def __init__(self, driver, base_url='http://www.example.com/'):
self.base_url = base_url
self.driver = driver
self.timeout = 30

def open(self):
self.driver.get(self.url)

Create a Login.py File:

#This class represents the login page which defines methods associated with the login page

Class LoginPage(BasePage):

def enter_username(self, username):
self.driver.find_element(By.ID, Locator.username_input).send_keys(username)

def enter_password(password):
self.driver.find_element(By.ID, Locator.password_input).send_keys(password)

def click_login_button():
self.driver.find_element(By.ID, Locator.submit_button).click()

Create a Base Test File for setup:

import unittest
from selenium import webdriver
import time
from time import sleep
import warnings
import urllib3

class testCases(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(10)
self.driver.maximize_window()

def tearDown(self):
if (self.driver != None):
print("Cleanup of test environment")
self.driver.close()
self.driver.quit()

if __name__ == "__main__":
unittest.main()

Create Login test case file:

from selenium import webdriver
import time
from time import sleep
import warnings
import urllib3


Class LogintTest(BaseTest):

def test_login(self):

login_page = Pages.LoginPage(self.driver)
login_page.open()
login_page.enter_username("testuser")
login_page.enter_password("testpass")
login_page.click_login_button()
assert driver.current_url == "http://example.com/home"
driver.quit()

Using the Page Object Model in this way makes it easier to write and maintain the test scripts because the test logic is separated from the UI elements. If the UI elements of the login page were to change, for example, the changes would only need to be made in the LoginPage class and would not affect the test logic. This makes it easier to maintain the test suite as the application under test evolves over time.

Overall, the Page Object Model is a valuable design pattern for test automation because it helps improve test scripts' maintainability, readability, and reusability, making it easier to develop and maintain a robust test automation framework.

I hope this article will help you, give a clap down below and also share with others.

Happy Testing! 😉

--

--

Bisma Latif

A writer, coder, and an avid reader, who puts her soul in everything she does!