The Agile Way

The Agile Way

Share this post

The Agile Way
The Agile Way
18 Refactoring: Extract to Page Object Model
Selenium Training Workbook

18 Refactoring: Extract to Page Object Model

Greatly enhance your automated test scripts' maintainability and readability.

Zhimin Zhan's avatar
Zhimin Zhan
Mar 21, 2023
∙ Paid
1

Share this post

The Agile Way
The Agile Way
18 Refactoring: Extract to Page Object Model
Share

Learning Objectives

  • Refactoring - Extract to Page Object Model

  • Autocomplete page functions

Test Data

Site: https://travel.agileway.net

Test Project: download here (or your existing one), and open in TestWise. We will be focus on the select flight tests (oneway and return trip) which we have done before.

The test script (before refactoring): `flight_spec.rb`

load File.dirname(__FILE__) + "/../test_helper.rb"
describe "Select Flights" do
  include TestHelper

  before(:all) do
    @driver = Selenium::WebDriver.for(browser_type, browser_options)
    driver.manage().window().resize_to(1280, 800)
    driver.get("https://travel.agileway.net")
    sign_in("agileway", "testwise")
  end

  before(:each) do
    driver.get("https://travel.agileway.net/flights/start")
  end

  after(:all) do
    driver.quit unless debugging?
  end

  it "User can select one way trip" do
    driver.find_element(:xpath, "//input[@value='oneway']").click
    Selenium::WebDriver::Support::Select.new(driver.find_element(:name, "fromPort")).select_by(:text, "Sydney")
    Selenium::WebDriver::Support::Select.new(driver.find_element(:name, "toPort")).select_by(:text, "New York")
    driver.find_element(:xpath, "//input[@value='Continue']").click
    expect(page_text).to include("Sydney to New York")
  end

  it "User can select return  trip" do
    driver.find_element(:xpath, "//input[@value='return']").click
    Selenium::WebDriver::Support::Select.new(driver.find_element(:name, "fromPort")).select_by(:text, "Sydney")
    Selenium::WebDriver::Support::Select.new(driver.find_element(:name, "toPort")).select_by(:text, "New York")
    Selenium::WebDriver::Support::Select.new(driver.find_element(:id, "departDay")).select_by(:text, "02")
    Selenium::WebDriver::Support::Select.new(driver.find_element(:id, "departMonth")).select_by(:text, "March 2021")
    Selenium::WebDriver::Support::Select.new(driver.find_element(:id, "returnDay")).select_by(:text, "03")
    Selenium::WebDriver::Support::Select.new(driver.find_element(:id, "returnMonth")).select_by(:text, "April 2021")
    driver.find_element(:xpath, "//input[@value='Continue']").click
    expect(page_text).to include("Sydney to New York")
    expect(page_text).to include("New York to Sydney")
  end
end

Tasks

In this exercise, I will introduce a few new concepts. Don’t worry, it is not hard. If you do not fully understand it yet, that’s OK, just know how to use it, like in the video below.

Knowledge Point: Page Object Model makes Automated Test Scripts easier to read and maintain

“Page Object is a Design Pattern that has become popular in test automation for enhancing test maintenance and reducing code duplication”. [Selenium Doc]

Let me illustrate it with an example. What do you think of the test script below:

It is not too bad, some may think. But it is not, from a maintenance perspective. Suppose, you have 100 test scripts like the above, and the user name textbox’s name is changed from `fromPort` to `fromCity` (on the flight page), what will happen? Will you do a global search and replace in 100 test script files, it’s not very good, isn’t it?

Now hold that thought. Have a quick look at the design below.

I break the test steps into four sections, each section corresponding to the user operations on a web page:

  • Home page

    • enter user name

    • enter password

    • click the sign in button

  • Flight page

    • Select from city

    • Select destination city

    • Click the next button

  • Passenger page

    • Enter the passenger's first name

    • Enter the passenger’s last name

    • Click the next button

  • Confirmation page

    • Assertion

Now accept there are Page Objects you can use, the “one-way trip” test case is changed to below:

  it "User can select one way trip" do
    flight_page = FlightPage.new(driver)
    flight_page.select_oneway_trip
    flight_page.select_from_city("Sydney")
    flight_page.enter_to_city("New York")
    flight_page.click_continue
    expect(page_text).to include("Sydney to New York")
  end

You might wonder where did the selenium steps go? In the `pages/flight_page.rb`.

require File.join(File.dirname(__FILE__), "abstract_page.rb")

class FlightPage < AbstractPage

  def initialize(driver)
    super(driver, "") # <= TEXT UNIQUE TO THIS PAGE
  end

  def select_oneway_trip
    driver.find_element(:xpath, "//input[@value='oneway']").click
  end

  def select_from_city(from_port)
    Selenium::WebDriver::Support::Select.new(driver.find_element(:name, "fromPort")).select_by(:text, from_port)
  end

  def select_to_city(to_port)
    Selenium::WebDriver::Support::Select.new(driver.find_element(:name, "toPort")).select_by(:text, to_port)
  end

  def click_continue
    driver.find_element(:xpath, "//input[@value='Continue']").click
  end
end

What are the benefits? Many, here I will just highlight one: much easier to maintain. This is related to the thought I asked you to hold earlier. If the `fromPort` is changed to `fromCity`, you just need to change one place (bold in the above), in `FlightPage`. That’s it, regardless of how many test scripts you have.

For more about Page Objects and Test Refactoring, check out my book “Practical Web Test Automation with Selenium WebDriver”.

In this article, I will show you the steps to make this change in TestWise.

Task 1: Extract to Page Function Refactoring in “Oneway trip” test case

Open the test project, and click the file `flight_spec.rb` to open it in the editor.

  1. Select the first user operation step, “click the oneway radio button”.

  2. Select the menu “Refactor” → “Extract to Page Function …”

  3. In the new “Extract to Page Function” dialog,

    1. type “FlightPage” in “Page Name”

    2. type “click_oneway_trip” in “Function Name”

    3. Click the “Refactor” button.

    Done, the first operation. The test fragment is changed (by TestWise) to this:

It is pretty easy and quick in TestWise, isn’t it?

Review

Keep reading with a 7-day free trial

Subscribe to The Agile Way to keep reading this post and get 7 days of free access to the full post archives.

Already a paid subscriber? Sign in
© 2025 Zhimin Zhan
Privacy ∙ Terms ∙ Collection notice
Start writingGet the app
Substack is the home for great culture

Share