Demistifying the page object model. Why should we care?

 

TLDR;

The page object model is a popular software design pattern for browser automation. It eliminates dependencies and redundancies in a systematic fashion. However, it takes more effort to build from scratch.

Note to reader: The code is written in python and uses the selenium library. This assumes that you have a basic understanding of both.

Also, the code uses dummy data so it won’t work. For working code, check my GitHub.

When we start learning about browser automation our script is designed in a procedural fashion. It looks like this.

#SCRIPT 1

#sample script for logging in a page

from selenium import webdriver

driver = webdriver.Firefox()
driver.get("www.login.com")
driver.find_element_by_id("username").send_keys("john")
driver.find_element_by_id("password").send_keys("123password")
driver.find_element_by_id("loginbutton").click()

Then we discover how awesome browser automation is! We giggle and show off to our friends.

Until we notice when we update our scripts, we keep on copy and pasting then editing the set of steps repetitively. Errors start to occur frequently and as we debug our scripts, frustration sets in. We finally get tired.

So, we find ways.

Our solution is to group the steps into a logical function.

A function is a block of code that performs an action and accepts parameters. It looks like this.

 


#sample script for logging in a page

from selenium import webdriver

def login_a_page(url, username, password):
    driver = webdriver.Firefox()
    driver.get(url)
    driver.find_element_by_id("username").send_keys(username)
    driver.find_element_by_id("password").send_keys(password)
    driver.find_element_by_id("loginbutton").click()

Look! Our first script has evolved to be a one-liner script that we can input parameters and re-use. Just call it. Like this.

#SCRIPT 2
login_a_page("www.login.com", "john", "123password")

 
How convenient is that? So it’s time to launch Spotify and rejoice to the latest house music.

After a while, we notice our scripts become longer, bigger and harder to read. There are still dependencies and redundancies in place. We become dissatisfied how inefficient our script is.

So, we research. We find out about design patterns. Specifically page objects.

We read and read and get confused until we try it out.

From our second script, we will update it to use a page object design pattern. It looks like this.


#sample script for logging in a page

from selenium.webdriver.common.by import By
from selenium import webdriver

class LoginPage():

#list of page elements
un = (By.ID, "username")
pw = (By.ID, "password")
login_btn = (By.ID, "loginbutton")

def __init__(self, driver):
    self.driver = driver

def go_to_url(self, url):
    self.driver.get(url)

def input_credentials(self, username, password):
    self.driver.find_element_by(*self.un).send_keys(username)
    self.driver.find_element_by(*self.pw).send_keys(password)

def click_login_btn(self):
    self.driver.find_element_by(*login_btn).click()

Now, we have applied object-oriented programming to our script.

At first glance, it looks confusing especially to new programmers but when we apply it, it will make sense. Notice our third version of our script.


#SCRIPT 3
login_page = LoginPage(webdriver.Firefox())
login_page.go_to_url("www.login.com")
login_page.input_credentials("john", "123password")
login_page.click_login_btn()

The script became a login page object and is similar to the first script but in a more readable, reusable and systematic fashion.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s