Last Updated on 2022-04-22 by James Croft

The page object pattern is one of many popularized solutions for UI test automation of applications. Its appeal is driven by the ability to develop a maintainable, object-oriented structure for your tests.

Reusability and maintainability are implemented through abstracting UI interaction to page objects rather than the tests themselves.

Page object pattern structure diagram

This ensures that changes to your application can easily be updated in a single place, rather than each test.

Given that a major part of your tests will consist of common interactions, implementing the page object pattern is an easy decision.

Example without usage

Here’s an example of a UI test which doesn’t take advantage of the page object pattern.

In this example, the code looks for a number box/stepper, clicks it, clears the text out, and inputs a new, expected value.

Even though it’s clear to understand what is happening here, just this simple snippet could be a part of a much larger test. Readability will become a problem very quickly.

Infact, a lot of the code here easily disrupts the purpose of the test, to set the value of the number box, then validate it.

If we imagine this requirement to set the number box being used across multiple tests, we end up with a copy and paste horror story and maintainability suffers.

Solving the problem with the page object pattern

The first thing to do is gain an understanding of your application structure. How many pages are there? What are you considering to be a page? Do you have any common layers which construct each page?

By identifying in your application what a page is, this will help lead you to build the same structure in your UI tests with page objects.

You also want to identify the key functions of your pages. For example, a login page might have three actions: inputting a username, inputting a password, and submitting.

What makes up a page object?

The page object is the abstraction of your test for the given page. This includes any element queries, as well as the key functions that make up the page.

These functions should be written in a human-readable way to ensure your written test methods are clear and concise. Here is an example of a login page as a page object in your UI tests.

In this page object, you’ll notice the following components:

  • Queries used to retrieve page elements.
  • Accessors for the page elements
  • Functions for common user actions of the page
  • Functions for assertion

As you can see, the construct of the page object is clearly defined and scoped to a single page within an application. Combined with the human-readable functions, this allows us to now write maintainable, easy-to-read tests.

Functions for assertion aren’t necessarily required within a page object. Some would argue that these can be left out in favor of functions which return values of the elements for assertions instead.

Notice that the functions return page object instances, usually itself. This allows functions to be chained in a test to encourage a readable flow.

It’s important that when a certain action within a page object navigates a user to a new page, that an instance of that page object is initialized and returned.

Writing your UI tests

With your page objects in place, you now have everything to write your tests.

Below is an example of a test that takes advantage of the above page object for the login page.

In this test, you can now understand the importance of the page object pattern.

The code required to retrieve elements and interact with them has been abstracted away into reusable functions. The tests are a lot clearer and easier to read. Most importantly, you can now ensure that any changes to your application UI are updated in a single place without affecting your tests.

Next steps

Now that you’ve understood how to implement the page object pattern in your UI tests, try it on a project!

Here are some helpful links to get more information on UI testing and other great articles on the page object pattern.

Found this article useful?

Thank you for taking the time to read this article. I’m committing to sharing more of my knowledge through my blog and open-source projects! You’ll also catch me in casual conversation on Bluesky and LinkedIn too.

If you enjoy what you see, please consider subscribing to get a notification when new articles go live!


Discover more from James Croft

Subscribe to get the latest posts sent to your email.

James Croft

James is a senior software engineer at Microsoft with over 10 years experience designing and building large-scale, distributed, cloud-native systems. He's deeply experienced in C#, Python, and TypeScript, with specialization in AI agent architectures, retrieval-augmented generation (RAG), and production-grade Azure systems.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.