In this post I would like to make you aware of the pitfalls of the classic page object approach and give insights in how to improve your page objects setup.
Classic approach
The Page Object pattern is very popular among automated testers. Its essence is that all the selectors by which the elements are searched for on the web page of the application under test are moved … exactly, into constants:
public class OrderPage {
@FindBy(name = "firstName")
public WebElement firstName;
@FindBy(name = "lastName")
public WebElement lastName;
@FindBy(name = "deliveryDate")
public WebElement deliveryDate;
}
Nice, huh? The good thing about such pagination objects is that you don’t have to duplicate the same locator in hundreds of different tests. When the locator changes, it only needs to be changed in one place.
And tests do not need to be changed often because they use a page object:
@Test
public void userCanOrder() {
OrderPage page = new OrderPage(webdriver);
firstName.sendKeys("Neil");
lastName.sendKeys("Jones");
deliveryDate.sendKeys("19-03-1986");
}
This approach became especially popular via Martin Fowler, who stated the ground rule for test automation:
The test should not contain any locator. Locators should be hidden in the page objects. If the locator is visible in the test, it is an invalid test.
What’s the catch?
We forget that it’s not just locators that change. The logic of working with elements is changing. Let’s say you have a hundred tests that contain this line:
deliveryDate.sendKeys("19-03-1986");
Yes, it’s kind of good if the locator changes – a hundred tests will remain intact.
But what if this element <input> suddenly turns into a calendar type control? You know, in which you first need to click the calendar icon, then select the year, then the month, and only then the day.
How to handle?
I tend to follow this rule:
You need to take out not constants, but logic.
The page object should not have a field deliveryDate, but a method.
public class OrderPage {
public void enterDeliveryDate(String deliveryDate) {
Selector(By.name("deliveryDate")).sendKeys(deliveryDate);
// you can implement the new calendar type here on one place!
}
}
Now, if the logic of working with the deliveryDate element (including the locator) changes, not a single test will change – only the page object. Of course, in this case, the fields of the page object must be private so that no one can use them directly, but only through the methods of the page object.
Happy testing!