Implementing Page Object Model for Playwright Tests

Implementing Page Object Model Playwright

Test automation eases the test execution in software development lifecycle but it comes with its own set of challenges. One of the most important aspects of writing effective automation suites is their maintainability. To be able to easily scale the frameworks it is very essential to write clear, modular and segregated scripts to minimise inter dependence. If the scripts are written without any format it can lead to a lot of overhead in their maintenance. This is where the Page Object Model comes to the rescue. In this article we will be discussing the Page Object Model and how we can leverage its benefits in Playwright tests.

What is a Page Object Model?

Page Object Model, or POM is an automation pattern that helps to create reusable components by encapsulating elements of a web page along with their interactions in a single class. These classes are a representation of each web page of the application and serve as reusable components.

Each page object class contains the implementation details to facilitate interaction with the user interface.

mobile app automation

What are the benefits of using Page Object Model?

Page Object Model comes with several benefits for creation of robust and scalable frameworks. Some of the advantages are discussed below-

  • Improves Test Code Maintainability
    • The test code becomes cleaner and easier to maintain by encapsulating page interactions.
    • If the UI is updated anywhere in the future, it is easy to update only the page class, reducing the need to make huge changes in the framework.
    • Whenever new tests are added, only page object methods are required without the need to work on element locators again.
    • Segregation between test code and the UI interaction code is promoted.
  • Promotes Reusability
    • Using page object classes, we can share the common actions across multiple tests.
    • Since page objects encapsulate reusable logic, code duplicity is reduced.
    • For example, a login page can be used for multiple tests using the login method.
  • Hides UI Implementation Details
    • Test methods or test scripts work with methods like login(), search(), etc. directly without the need to expose the code within these methods.
    • Test scripts do not directly interact with the UI elements, hence protecting the scripts from the effect of UI changes.
  • Robust Tests, Less Vulnerable to UI Changes
    • The test scripts are less brittle to UI changes, if any UI element is updated, only the page object class needs to be updated.
    • As compared to updating many locators spread across the script, it is very easy to maintain the scripts using POM.
  • Clean Separation of Test Code and Page Interactions
    • The test cases logic is focused on validations rather than UI details.
    • The test actions are translated to UI interactions using the page object.
    • Separation makes code modular and easier to maintain.

What are the disadvantages related to Page Object Model?

While the Page Object Model provides a lot of benefits, there are some potential drawbacks that one should consider.

  • Complexity due to additional classes
    • Other than maintaining the tests, additional page object classes are introduced.
    • More files and classes are created in the project structure.
    • New members might take some time to understand the page object structure.
  • Page Object needs to sync with UI
    • If there is any update in the UI, the page object needs to be updated to accommodate the changes.
    • If any additional feature is added to the web page, corresponding page objects might need addition of new methods to handle the same.
    • Using page objects will not eliminate the need of maintenance, any change in the flow, locators will still require maintenance of the page objects.
  • Abstraction layer is added
    • Although abstraction helps hide UI implementation details, it can become difficult to access or verify elements at times.
    • It can sometimes lead to difficulty in debugging issues.

Now that we understand what page object model is and how it can be helpful to us, let us see how we can implement it in our Playwright Tests.

Implementing Page Object Model for Playwright Tests

To begin writing Playwright tests, ensure that you have downloaded and installed Visual Studio Code. Also, download and install Node JS in your system.

Let us now look through the steps to creating the structure of our Playwright Page Object Model project.

  1. Create a new directory for the project. I have created a directory PlaywrightPOM in my system.
  2. Next, we open the directory in Visual Studio Code(VS Code)we created in Step#1. Click on File > Open > Select the directory name you have created(PlaywrightPOM).
    You will see it in the VS Code explorer as shown below-
playwright install

3.We next install playwright, by clicking on Terminal > New Terminal. In the terminal write the below command:

npm init playwright@latest

This command will prompt you to answer certain questions, select them as per your requirement.

Once the above command is executed, you will see that files and folders have been added to the project as shown in the snapshot below-

image4 1

4. Next, we will create two more folders viz, pages and utilities in the project directory. The pages folder will hold the page objects of our page object model. The Utilities folder on the other hand will contain common utilities(if any) for the project.

image3 2

5. Before we start writing the scripts we will install the browsers by entering the below command in the terminal:

npx playwright install

6.We are now all set with our project structure and we will now start writing our tests, but before that let us see the use case steps that we will be using to implement the POM for Playwright Test.

a. Navigate to testgrid.io.

b. Click on the Get Started button.

c. Enter details in the Sign Up Form.

Note that you can automate your own use case by following the approach mentioned below.

7.To achieve this we will be using Typescript. Let us first create one file inside the pages folder and name it home.page.ts. In this file we will write our code to locate the elements on the page and perform actions on them:


home.page.ts

import { expect, Locator, Page } from '@playwright/test';
export class TestGridHomePage{
readonly url="https://testgrid.io/";
readonly page: Page;
readonly getStartedButton: Locator;


constructor(page: Page){
this.page = page;
this.getStartedButton = page.locator('text=Get started');
}


async goTo(){
await this.page.goto(this.url);
}


async getStarted(){
await this.getStartedButton.click();
}

}

Code Walkthrough

import { expect, Locator, Page } from '@playwright/test';

Imports Playwright test library classes that will be used in the page object

export class TestGridHomePage{

Defines the TestGridHomePage class that will contain the page object logic

readonly url="https://testgrid.io/";
readonly page: Page;
readonly getStartedButton: Locator;

Read only fields are declared where-

  • Url, is used to declare the TestGrid base url.
  •  Page, field of type Page that will reference the Playwright Page object.
  • getStartedButton, locator field to store the Get Started button locator.
constructor(page: Page){
this.page = page;
this.getStartedButton = page.locator('text=Get started');
}

Constructor is created to accept the Page object and initialise the page and locator fields.

async goTo(){
await this.page.goto(this.url);
}

goTo() method is used to navigate to the base url.

async getStarted(){
await this.getStartedButton.click();
}

getStarted() method is used to click on the Get Started button.

We will create another page class, viz, register..page.ts that will be used to hold the elements of the Sign Up page that we see after clicking on the Get Started button. The code for the same will be:

register.page.ts

import { expect, Locator, Page } from '@playwright/test';
export class RegistrationPage{
readonly page: Page;
readonly fullName: Locator;
readonly email: Locator;
readonly mobile: Locator;
readonly password: Locator;
readonly signUpButton: Locator;


constructor(page: Page){
this.page = page;
this.fullName = page.locator('id=full_name');
this.email = page.locator('id=business_email');
this.mobile = page.locator('id=mobile_number');
this.password = page.locator('id=password');
this.signUpButton = page.locator('id=signin-button');
}


async fillForm(){
await this.fullName.fill("Your Name");
await this.email.fill("Your email id");
await this.mobile.fill("Your mobile number");
await this.password.fill("Your choice password");
await this.signUpButton.click();
}


}

Code Walkthrough

readonly page: Page;
readonly fullName: Locator;
readonly email: Locator;
readonly mobile: Locator;
readonly password: Locator;
readonly signUpButton: Locator;

Read only fields are declared for the page, and the sign up form elements.

constructor(page: Page){
this.page = page;
this.fullName = page.locator('id=full_name');
this.email = page.locator('id=business_email');
this.mobile = page.locator('id=mobile_number');
this.password = page.locator('id=password');
this.signUpButton = page.locator('id=signin-button');
}

A constructor is created to initialise the page and its elements using the locators.

async fillForm(){
await this.fullName.fill("Your Name");
await this.email.fill("Your email id");
await this.mobile.fill("Your mobile number");
await this.password.fill("Your choice password");
await this.signUpButton.click();
}

The fillForm() method is used to fill in the details to the sign up form. You can add more lines of code to the method to handle specific scenarios in your case.

8. The next step is to write the test file to execute our test case. We create home.test.ts file and write the below code in it:

import{test,expect} from '@playwright/test';
import { TestGridHomePage } from "../pages/home.page";
import { RegistrationPage } from '../pages/register.page';
test('TestGrid Get Started Flow',async ({page}) => {
const homePage = new TestGridHomePage(page);
const regPage = new RegistrationPage(page);
await homePage.goTo();
await homePage.getStarted();
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
await delay(10000);
await regPage.fillForm();
})

Code Walkthrough

import { TestGridHomePage } from "../pages/home.page";
import { RegistrationPage } from '../pages/register.page';

We import the required pages in the test.

const homePage = new TestGridHomePage(page);
const regPage = new RegistrationPage(page);

References to the pages of the POM are created using which we will refer the methods of the page classes.

await homePage.goTo();
await homePage.getStarted();

The goTo() and the getStarted() methods of the home page are called to navigate to the url and click on the Get Started Button respectively.

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
await delay(5000);

We are using promise to set a timeout to handle network lags here. You may use other ways to handle timeout scenarios in your project as per your requirement.

await regPage.fillForm();

  • Finally, we call the fillForm() method to fill up the details in the sign up form using the registration page object.

Once these files are created, your project directory would look like below-

image6 2

9.You can now execute this test file using the below command:

npx playwright test home.test.ts –headed

By default, playwright executes in headless mode, hence use –headed to execute the test in headed mode. Once this command is executed you will see that your test is executed and the results are displayed in the console as below-

image1 3

Additionally you can look at the generated report to look at the results of execution:

image2 2

Conclusion

To summarise Page Object Model, we can conclude on below points:

  • Page Object Model, POM is a design pattern that helps to create a robust and maintainable automation framework.
  • Due to the ease of handling the scripts and segregating the test logic from the UI interactions, POM helps in creating efficient and scalable frameworks.
  • POM eases the process of updating the page objects in case of changes in the UI or the flow, by just having the need to update the page object classes with minimal need to touch the test scripts.
  • Although POM makes the tests more readable and maintainable, it might sometimes create problems in debugging issues.
  • POM when implemented in a clear and concise manner can help in creating modular frameworks, reusing page objects wherever necessary.


Leave a Reply

Your email address will not be published. Required fields are marked *

Tweet
Share
Share
Pin