Implementing Page Object Model for Playwright Tests
In this article, we will be discussing the Page Object Model and how we can leverage its benefits in implementing Playwright tests.
Join the DZone community and get the full member experience.
Join For FreeTest automation eases the test execution in the 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 scale the frameworks easily, it is essential to write clear, modular, and segregated scripts to minimize 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.
What Are the Benefits of Using the Page Object Model?
Page Object Model comes with several benefits for the 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 the 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 case 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 the 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 the addition of new methods to handle the same.
- Using page objects will not eliminate the need for maintenance of any change in the flow, and 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 the 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 called 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:
3. We next install the 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 and select them as per your requirements.
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:
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.
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 the Playwright Test.
- Navigate to testgrid.io.
- Click on the Get Started button.
- 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;
- 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 initialize 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 signup 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 initialize 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 of the signup 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 a 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();
})
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);
await homePage.goTo();
await homePage.getStarted();
The
goTo()
and thegetStarted()
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 a 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 requirements.
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 will look like the below:
9. You can now execute this test file using the below command:
npx playwright test home.test.ts –headed
By default, the 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:
Additionally, you can look at the generated report to look at the results of execution:
Conclusion
To summarise the 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.
Published at DZone with permission of Gunjan Kaushik. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments