5

Implementing the page object pattern in UI tests

 3 years ago
source link: https://www.jamescroft.co.uk/implementing-the-page-object-pattern-in-ui-tests/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Implementing the page object pattern in UI tests
Coding

Implementing the page object pattern in UI tests

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 a no-brainer.

Example without usage

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

string expected = "1500";

var numberBox = this.driver.FindElement(By.XPath(".//*[@AutomationId='MyNumberBox']")); numberBox.Click(); numberBox.Clear(); numberBox.SendKeys(expected); numberBox.SendKeys(Keys.Enter);

numberBox.GetAttribute("RangeValue.Value").ShouldBe(expected);

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 distrupts 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 applications 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 3 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.

namespace MyApp { using Legerity.Pages; using Legerity.Windows.Extensions; using OpenQA.Selenium; using OpenQA.Selenium.Appium.Windows; using Shoudly;

public class LoginPage : BasePage { private readonly By usernameQuery = ByExtensions.AutomationId("LoginUsernameBox"); private readonly By passwordQuery = ByExtensions.AutomationId("LoginPasswordBox"); private readonly By submitQuery = ByExtensions.AutomationId("LoginSubmitButton");

public LoginPage() { }

public WindowsElement Username => this.Driver.FindElement(this.usernameQuery); public WindowsElement Password => this.Driver.FindElement(this.passwordQuery); public WindowsElement Submit => this.Driver.FindElement(this.submitQuery);

protected override By Trait => By.XPath(".//*[@Name='Login'][@AutomationId='TitleTextBlock']");

public LoginPage SetUsername(string username) { this.Username.Click(); this.Username.Clear(); this.Username.SendKeys(username); return this; }

public LoginPage SetPassword(string password) { this.Password.Click(); this.Password.Clear(); this.Password.SendKeys(password); return this; }

public MainPage Login() { this.Submit.Click(); return new MainPage(); }

public LoginPage VerifyUsername(string username) { this.Username.Text.ShouldBe(username); return this; }

public LoginPage VerifyPassword(string password) { this.Password.Text.ShouldBe(password); return this; } } }

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 will navigate 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.

namespace MyApp.Tests { using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using MyApp;

[TestClass] public class WhenLoggingIn : BaseTestClass { [TestMethod] public void ShouldAuthenticateValidUser() { const string username = "jamescroft"; const string password = "Pass@w0rd1";

LoginPage loginPage = new LoginPage(); loginPage.VerifyPageShown();

loginPage .SetUsername(username) .SetPassword(password);

loginPage .VerifyUsername(username) .VerifyPassword(password);

MainPage mainPage = loginPage.Login(); mainPage.VerifyPageShown(timeout: TimeSpan.FromSeconds(2)); } } }

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 updating 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, give it a try on a project!

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK