Skip to content

Selenium WebDriver: Implementing PageObject in Python

March 18, 2013

Selenium logo

Hi, here is short description of Selenium Webdriver PageObject pattern (http://code.google.com/p/selenium/wiki/PageObjects) implementation in Python.

First of all let’s create some base classes for further use:

WebDriver wrapper to launch the only instance of driver in the same time:

from selenium import webdriver

class SeleniumWrapper:
  _instance = None

  def __new__(cls, *args, **kwargs):
    if not cls._instance:
      cls._instance = super(SeleniumWrapper, cls).__new__(cls, *args, **kwargs)
    return cls._instance

  def connect(self, host="https://dev.host.com/"):
    self.driver = webdriver.Firefox()
    self.base_url = host
    return self.driver

Base page object and page element classes:

import unittest
import time

class BasePageObject(unittest.TestCase):
def wait_for_element_displayed_by_id(self, driver, locator, timeout=60, msg="[error] element is not found" ):
  for i in range(timeout):
    try:
      if driver.find_element_by_id(locator).is_displayed(): break
    except:
      pass
    time.sleep(1)
  else:
    self.fail(msg)
class BasePageElement(object):
  def __get__(self, obj, cls=None):
    pass

  def __delete__(self, obj):
    pass

In package where your base classes located you need to put SeleniumWrapper class instance creation into __init__.py file, e.g.:

from pageobjects.base.seleniumwrapper import SeleniumWrapper
selenium_driver = SeleniumWrapper()

So let’s look at the basic login page, here is pageobject class for it:

from pageobjects import locators
from pageobjects.base import selenium_driver
from pageobjects.base.basepageobject import BasePageObject
from pageobjects.base.basepageelement import BasePageElement

class UsernameElement(BasePageElement):
  def __init__(self):
    self.locator = locators["login.username"]

  def __set__(self, obj, val):
    driver = selenium_driver.driver
    driver.find_element_by_id(self.locator).send_keys(val)

class PasswordElement(BasePageElement):
  def __init__(self):
    self.locator = locators["login.password"]

  def __set__(self, instance, value):
    driver = selenium_driver.driver
    driver.find_element_by_id(self.locator).send_keys(value)

class LoginPageObject(BasePageObject):
  username = UsernameElement()
  password = PasswordElement()

  def __init__(self, driver, base_url):
    self.driver = driver
    self.driver.get(base_url)
    driver.find_element_by_id(locators["login.open"]).click()
    self.wait_for_element_displayed_by_id(self.driver, "id_username")
    self.assertEqual("Login", self.driver.title)

  def submit(self):
    self.driver.find_element_by_id(locators["login.submit"]).click()
    self.wait_for_page_title(self.driver, "Profile", msg="[error]: we are not logged in")

It is good practice to put all elements locators into one place, e.g.:

locators = {}
locators["login.open"] = "login-open"
locators["login.username"] = "id_username"
locators["login.password"] = "id_password"
locators["login.submit"] = "button-login"

What’s next? We need to run tests:

import unittest
from pageobjects.base import selenium_driver
from pageobjects.login.loginpageobject import LoginPageObject
from pageobjects.page.aboutpageobject import AboutPageObject

class AboutPageTests(unittest.TestCase):
  def setUp(self):
    self.verificationErrors = []
    self.driver = selenium_driver.connect()
    self.driver.implicitly_wait(10)
    self.base_url = selenium_driver.base_url

  def tearDown(self):
    self.driver.quit()
    self.assertEqual([], self.verificationErrors)

  def test_login(self):
    lpo = LoginPageObject(self.driver, self.base_url)

    lpo.username = "mail@mail.info"
    lpo.password = "pwdpwd123"
    lpo.submit()

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

Good luck!
P.S.: If you need to run your tests beyond CI (e.g. Jenkins) use Corey Goldberg blog post Python – Headless Selenium WebDriver Tests using PyVirtualDisplay

UPD: Source code on GitHub https://github.com/tjlee/selenium_example

Advertisements
2 Comments
  1. pcp permalink

    Actually, here’s now a Python package for implementing the PageObjects design:
    http://holmiumcore.readthedocs.org/en/latest/index.html

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 )

Google+ photo

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

Connecting to %s

%d bloggers like this: