Joseph Wilk

Joseph Wilk

Programming bits and bobs

Page Object Pattern

What Is the Page Object Pattern?

The Page model is a pattern that maps a UI page to a class, where for example a page could be a HTML page. The functionality to interact or make assertions about that that page is captured within the Page class. Then these methods may be called by a test. So ultimately we are introducing a gatekeeper to the GUI of a page.

Why use the Page Object Pattern?

  • Readable dsl for tests

  • Promotes Reuse

  • Centralise UI coupling – One place to make changes around the UI.

Implementing the Page Pattern in Cucumber

Within Cucumber there are two main ways we can encapsulate the page UI:

The Page Object Pattern

features/pages/login_page.rb

1
2
3
4
5
6
7
8
9
10
11
class LoginPage
  def login(user, password)
    fill_in :user, user
    fill_in :password password
    click 'login'
  end

  def visit
    visit "/login"
  end
end

features/step_definitions/user_steps.rb

1
2
3
4
5
6
Given /^I login with username "Joseph" and password "cuker"$/ do |username, password|
  login_page = LoginPage.new

  login_page.visit
  login_page.login(username, password)
end

The Page Step definition Pattern

Cucumber step definitions are all defined at the same scope, but we use folders and files to create logical organisation. We can create folders for UI step definitions and domain step definitions.

  • features/domain/step_definitions/*

  • features/ui/step_definitions/*

We create a step definition file mapping to a UI page. features/ui/step_definitions/login_page_steps.rb

1
2
3
4
5
Given /^I login with username "Joseph" and password "cuker"$/ do |username, password|
  fill_in :user, user
  fill_in :password password
  click 'login'
end

Whats the right way to encapsulate the UI?

Just using step definitions for organisation within a project can have a number of problems:

Global scope within Cucumbers world Instance variables are global across all step definitions

1
2
3
Given /^I mess with scope$/ do
  @this_can_be_seen_by_every_other_step = 'uh oh'
end

Managed and run through Cucumber No easy way to be reused outside of Cucumber or test in isolation. By isolating the test code we can easily provide adapters for reuse in different test frameworks (for example similar to what email-spec does).

The Page Object pattern (and adding another layer of abstraction) has a couple of nice properties:

  • Bounded scope (if you use your classes/objects nicely)

  • Isolated units that can be invoked and controlled independently of overarching testing framework

Should I be using the Page Object Pattern?

Yes, No, Maybe.

Extra layers of abstraction introduce complexity and so the Page Object Pattern should be used carefully when there is a sufficiently high burden of maintenance (which usually means lots of step definitions).

Its important outside of the Page object pattern to realise the weaknesses of just using step definitions as your only modelling tool. Irrelevant of what metaphor you decide to organise around it’s a good habit to push the code out of the step definitions.

Comments