Page Object Model

Page Object Model

Why they are important

Page Object Model (or pattern) is the de-facto model for how the developer should write automation tests. It’s a framework to separate the business logic from the simulated user actions to execute a workflow.

A page object is an abstract representation of a single webpage. The page object should contain the depiction of the elements on the page, found by locators. Methods within the page object should contain simple user actions (get, set, click, submit) using the locators. These actions can be woven together for a test to interact with a page.

  • Enter the username
  • Enter the password
  • Click the login button

These actions can be wrapped up in a method called login, which may accept a username and a password. This login method would belong to a page object called “LoginPage”. Together, this would look like

loginPage.login(username, password)

Now, any test that requires a user to log in can initialize the LoginPage object and call the login method. This pattern allows for a single page object to be used across many tests to reduce the amount of code needed to write tests. Following this pattern also reduces the required maintenance. Any change that gets made to a page object affects all tests that use that page object.

Creating Page Objects

Page object methods should represent actions or small sets of actions that a real user could do within a few seconds. Entering a string into a text box is an example of a good page method. Performing a long or complicated series of actions is not acceptable and should be broken up into smaller, more simple sets of actions.

Page-logic should be self-contained. Any logic which crosses the boundary into another page object probably means it is testing-logic and it should be extracted. In the spirit of reusing things, we’ll reuse the login page example. A bad idea would be to include a method called clickAccountSettings in the LoginPage class because that’s only an item to a logged in user. If the user is logged out, there are no account settings that can be interacted. An example of how to better do that is to have a second page object called LandingPage that contains the clickAccountSettings method. The test would then contain the following two lines:

landingPage = loginPage.login(username, password)
landingPage.clickAccountSettings()

Hierarchy of a Page Object

A page object class should extend a BasePage. The BasePage should have locators and methods to interact with any common elements across all pages. In reality, some pages may not have a navbar (think of the login page). If the page object contains that, but the test author knows not to interact with it, that is fine. It’s a stylistic preference.

The other approach for that situation is to have BasePage and CompanyBasePage. CompanyBasePage inherits from BasePage. LoginPage would inherit from BasePage and almost all other pages from CompanyBasePage. This approach adds another layer of hierarchy that comes in handy if a single framework handles multiple applications. Suppose the company has Product A and Product B. The two products contain different web pages. The benefit of this approach would be that any function in BasePage can apply to both products – whether that’s a function to find items in a list or to open a database connection – the updates apply to both products.

Creating Locators

The easiest way to create a locator is to first find the element you want on a page using Chrome or Firefox.

  • Right-click on the element and select Inspect.
  • Right-click on the highlighted element in the Devtools tab and then click on Copy -> CSS Path

You’re now able to locate elements by CSS or XPath. Any matching elements get highlighted in the Devtools tab. You should use this method when creating a page to make sure the selectors you are creating are valid. The longer and more convoluted a locator is, the more likely it breaks with page changes. Try to use the simplest selector you can. From the order of most preferred to least preferred:

  • A custom HTML attribute such as “data-locator,” “se-locator” or whatever name you choose.
  • Id
  • CssSelector
  • Xpath
  • Name

Don’t use an element’s text to define its selector. While it may seem straightforward, it breaks each time the text changes. It’s also a non-scalable approach if A/B testing is involved, as the wording of text often changes. This approach also fails when testing multiple languages.

The Page Constructor

The page constructor is the only method in a page object that should contain assertions. This approach ensures that the page gets loaded with the correct information present. In additions to assertions, shared elements and objects should be instantiated in the constructor to reduce the re-fetching of those elements.

Things to check in a constructor

  • Are important elements visible?
  • Are the expected buttons correctly enabled/disabled?
  • Are the breadcrumbs displayed?

Example page constructor

By MENU_BUTTON = By.cssSelector("div[se-locator='menu']");

public SomePage(Webdriver driver) {
    menuButton = getWebElement(MENU_BUTTON);
    assertTrue(menuButton.isDisplayed());
    assertTrue(menuButton.isEnabled());
}

Page Methods

Page methods should always return information to the user about the page or perform actions on the page. There should be no assertions within any of the page methods. For example, if the user entered text into a field, the method that put it there should return what was recorded. This process allows a test to check if all of the characters were entered.

Common Page Methods

Page methods acting as an interface between a single user action and a test means that the methods are pretty boring. You only check if something is visible or enter text a finite number of ways. Each page object will have methods that wrap the important fields on the page that need to be interacted with.

isErrorDisplayed()
isSubmitButtonEnabled()
getName()
clickSubmitButton()

All of these page-specific methods boil down to relying on a few commonly used Selenium bindings. First the element is located (returning a WebElement object) and then the object is acted upon.

isDisplayed()
isEnabled()
getText()
click()
Comments are closed.