Agile Testing Using Page object models

Abhishek Dhoundiyal
Feb 4, 2021 · 7 min read

Page Object is a Design Pattern which has become popular in test automation for enhancing test maintenance and reducing code duplication. A page object is an object-oriented class that serves as an interface to a page of your AUT. The tests then use the methods of this page object class whenever they need to interact with the UI of that page. The benefit is that if the UI changes for the page, the tests themselves don’t need to change, only the code within the page object needs to change. Subsequently, all changes to support that new UI are located in one place.

The Page Object Design Pattern provides the following advantages:

  • There is a clean separation between test code and page specific code such as locators (or their use if you’re using a UI Map) and layout.
  • There is a single repository for the services or operations offered by the page rather than having these services scattered throughout the tests.
  • For enhancing test maintenance and reducing code duplication.

In both cases, this allows any modifications required due to UI changes to all be made in one place.

What Does a Page Object Look Like?

First, consider an example, that does not use a page object:

/***
* Tests login feature
*/
public class Login {

public void testLogin() {
// fill login data on sign-in page
driver.findElement(By.name("user_name")).sendKeys("testUser");
driver.findElement(By.name("password")).sendKeys("my supersecret password");
driver.findElement(By.name("sign-in")).click();

// verify h1 tag is "Hello userName" after login
driver.findElement(By.tagName("h1")).isDisplayed();
assertThat(driver.findElement(By.tagName("h1")).getText(), is("Hello userName"));
}
}

I know it look looks simple?

But are you sure?

This sort of basic approach is killing our quality automation script.

So what the actual problem or issue in this approach?

There are two basic problems.

  • There is no separation between the test method and the AUT’s locators; both are present in a single method. If the application UI changes its identifiers, layout, or elements the test itself must change.
  • The Element locators would be spread in multiple tests, in all tests that had to use this login page.

Applying the page object techniques, this example could be rewritten like this in the following example of a page object for a Sign-in page.

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

/**
* Page Object encapsulates the Sign-in page.
*/
public class SignInPage {
protected static WebDriver driver;

// <input name="user_name" type="text" value="">
private By usernameBy = By.name("user_name");
// <input name="password" type="password" value="">
private By passwordBy = By.name("password");
// <input name="sign_in" type="submit" value="SignIn">
private By signinBy = By.name("sign_in");

public SignInPage(WebDriver driver){
this.driver = driver;
}

/**
* Login as valid user
*
* @param userName
* @param password
* @return HomePage object
*/
public HomePage loginValidUser(String userName, String password) {
driver.findElement(usernameBy).sendKeys(userName);
driver.findElement(passwordBy).sendKeys(password);
driver.findElement(signinBy).click();
return new HomePage(driver);
}
}

and page object for a Home page could look like this.

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

/**
* Page Object encapsulates the Home Page
*/
public class HomePage {
protected static WebDriver driver;

// <h1>Hello userName</h1>
private By messageBy = By.tagName("h1");

public HomePage(WebDriver driver){
this.driver = driver;
if (!driver.getTitle().equals("Home Page of logged in user")) {
throw new IllegalStateException("This is not Home Page of logged in user," +
" current page is: " + driver.getCurrentUrl());
}
}

/**
* Get message (h1 tag)
*
* @return String message text
*/
public String getMessageText() {
return driver.findElement(messageBy).getText();
}

public HomePage manageProfile() {
// Page encapsulation to manage profile functionality
return new HomePage(driver);
}
/* More methods offering the services represented by Home Page
of Logged User. These methods in turn might return more Page Objects
for example click on Compose mail button could return ComposeMail class object */
}

So now, the login test would use these two-page objects as follows.

/***
* Tests login feature
*/
public class TestLogin {

@Test
public void testLogin() {
SignInPage signInPage = new SignInPage(driver);
HomePage homePage = signInPage.loginValidUser("userName", "password");
assertThat(homePage.getMessageText(), is("Hello userName"));
}

}
Page object example

There is a lot of flexibility in how the page objects may be designed, but there are a few basic rules for getting the desired maintainability of your test code.

Page objects themselves should never make verifications or assertions. This is part of your test and should always be within the test’s code, never in an page object. The page object will contain the representation of the page, and the services the page provides via methods but no code related to what is being tested should be within the page object.

There is one, single, verification which can, and should, be within the page object and that is to verify that the page, and possibly critical elements on the page, were loaded correctly. This verification should be done while instantiating the page object. In the examples above, both the SignInPage and HomePage constructors check that the expected page is available and ready for requests from the test.

A page object does not necessarily need to represent all the parts of a page itself. The same principles used for page objects can be used to create “Page Component Objects” that represent discrete chunks of the page and can be included in page objects. These component objects can provide references to the elements inside those discrete chunks, and methods to leverage the functionality provided by them. You can even nest component objects inside other component objects for more complex pages. If a page in the AUT has multiple components, or common components used throughout the site (e.g. a navigation bar), then it may improve maintainability and reduce code duplication.

There are other design patterns that also may be used in testing. Some use a Page Factory for instantiating their page objects.

What About Automation Testing in Agile?

Agile Testing can begin at the start of the project with continuous integration between development and testing. Agile Testing is not sequential (in the sense it’s executed only after coding phase) but continuous.

Automation challenges with agile software development.

  • New features are introduced quickly, which reduces the available time for test teams.
  • Very less time to prepare a test plan
  • Requirement changes and updates are inherent in an agile method, becoming the biggest challenge for QA.

Page Object Model when Applied to Agile Testing

Because of the reusability and modularization, it introduces, the page object model creates scalable frameworks that are agile to changes made in each release of the application.

So if an element changes and the element locator is no longer valid we only must update the relevant location within the page object in one singular location. This is a huge time-saving feature brought about by decomposing the framework into logical components; for those from a development background, this will ring true of object-oriented programming design principles.

If any of the elements change and the locators are no longer valid we must scan through each test script to find the affected components and update them one by one in many locations. Using the page object model, we abstract from this direct connection to the automation execution engine.

Why Should We Use This — Isn’t the Future Scriptless Automation?

Over the past decade, we have seen the testing industry move between numerous trends for test automation creation.

Keyword-driven automation/scriptless frameworks is a recent trend that has seen many failures within organizations. The increasing problem with these types of frameworks is that applications are complex and aren’t necessarily as simple as clicking a few buttons and entering the text within a form. Often, decisions we make later in an end-to-end scenario will be dependent on data or decisions made previously. In such scenarios (which are very common on any real-world application with embedded rules), custom code must be implemented.

We are now seeing the rise of the automation framework which is entirely code-based, with a view to tackling the problem of application complexity. This is something that can’t be addressed by scriptless frameworks unless significant customization takes place. This customization defeats the purpose of such frameworks. The page object model allows us to embed custom code within each method and abstract away from it. Each method may be performing trivial actions against a UI or might feature complex code which is performing advanced operations like going into backend databases, performing API requests, handling email approvals, spinning up environments, and more.

The key premise is that the page object model facilitates and hides the complexity of the routines taking place and exposes them in an elegant interface that can be used in a test scenario with ease. This abstraction is the power of a code-based automation framework coupled with the page object model, the only real viable option when building test automation for complex systems.

Geek Culture

Proud to geek out. Follow to join our +1.5M monthly readers.