Structuring functional tests with the Page Object Model

What is the Page Object Model, otherwise known as POM? Most tutorials describe the Page Object Model as getting all your HTML locators into a single central location and that’s it. In my opinion, however, implementing the POM requires you to have a one to one correspondence between functional components in your system under test and your automated tests.

For example, if you have a component for login, then your test suite should contain a test component that contains all the functionality required for login tests, like, HTML locators and methods to enter username, password and click on a login button, at a single location.

If, as in the funda website, you can login from the home page as well as from the page displaying properties, you can then have the home page and the property page (object detail page) inherit the login functionality.

This creates a logical structure that is easy to understand, maintain and extend. It also promotes code reuse.
The below diagram explains the concept, whereby the Header common functionality across all funda and funda in business webpages is put into Common Base and inherited by all webpages. The Search widget is used only in the home page, hence it is put into Component Base and inherited by the home page.

Later on, suppose the Search widget is introduced in the property (object detail) pages then creating tests for this would simply mean inheriting the Search widget by the property pages.

In this way, if we create a self check test verifying visibility at the highest level of implementation (here being the Page Base), when a page is instantiated, all the components in it would be checked if they are displayed. So, whenever your test navigates to a certain page, you can be certain that the visibility of it components will be checked.

What are the steps to do to implement POM?

You need to make the following classifications in order to arrive at an architecture for your tests:

  • Functionality
    • Define categories of objects/object groups that represent a certain functionality on the website.
    • Further categorize these groups into higher-level objects like pages, frames and popups.
    • Make a note if this category appears in multiple locations.
  • Structure
    • Define what objects are present in each group and functions to get and set them.
    • Define the HTML identifiers of the objects in each group.
    • Ensure that the HTML identifiers of the objects on the website are independently defined.
  • Behavior
    • Define what each category does. What happens in each group?
    • What is the expected positive behavior? What are the probable negative scenarios for this category?
  • Initiation and termination
    • Define how we come to each category and how we finish with it, and also from where the initiation and termination occurs.
    • Define initiation and termination functions in step classes of the category from where it occurs.
  • Interaction
    • Define how different categories interact with each other in the step classes.
    • Make a note if your category will interact with other categories from different teams.

Defining Functions and Tests

One important thing to note is that having the functions in you tests return a webpage or component of your webpages proves to be very useful to reuse in later steps of the test. For example, in the below snippet of code, the first line of code uses a function that returns an object of type HomePage. Using homePage, we call NavigateToResultPage function, which returns an object of type ResultPage. The test finishes by asserting that the actual title of the result page is as expected.

[Test]
[Category("Pretest funda")]
public void Request_funda_resultpage()
{
    var homePage = new PageObjects.Funda.Base(Driver, _baseUrl);        
    var resultPage = homePage.NavigateToResultPage();

    string expectedTitle = "Huizen te koop in heel Nederland";

    string actualTitle = resultPage.GetPageTitle();

    Console.WriteLine("Checking expected title: {0} to actual title: {1}", expectedTitle, actualTitle);

    Assert.AreEqual(expectedTitle, actualTitle, string.Format("Expected title: {0} is not the same as actual title {1}", expectedTitle, actualTitle))
}

Below is also an example of writing a function, which returns an object that is either a web page or a component.

public ResultPage NavigateToResultPage()
{
    var page = new ResultPage(Driver, BaseUrl);
    page.Open();
    return page;
}

The Future of POM?

POM has many advantages as discussed, they being, code reuse, maintainability, extensibility and understandability. The question remains to see if we can extend POM to a site or even a channel object model.

written by Jome Princil, QA Engineer at funda.