How to apply Playwright Fixtures with Page Object Model

How to apply Playwright Fixtures with Page Object Model

Web automation testing is an essential part of software testing. To automate the tests, we can use various automation testing frameworks. Playwright is one such popular testing framework that is widely used to automate web applications .In this article, we will explore how we can use the Page Object Model (POM) and Fixtures with Playwright to write efficient and maintainable web automation tests.

Applying Page Object Model into your existing playwright code is a topic which is widely available in all over the web. So I’m not going to go into detail about that topic but I will explain what was the issue I faced when I started using playwright with Page Object Model and what is the solution I found.

So in my story I was assigned to do a feasibility study for one of our clients and there I started playwright with just writing and organizing all tests inside test file then gradually changed it by applying Page Object Model design pattern to organize the code into reusable components. Even after I applied Page Object Model I felt some part of my code is duplicating as in every test case I had to initialize page objects. Then I was finding a solution and ended up with fixtures which is a cool feature provided by playwright.

Now, let’s see how we can use fixtures to avoid this code duplication in Playwright and write efficient web automation tests.

Page Object Model without Fixtures

I have created loginPage and dashboardPage as my pages and created three test cases in my test runner using them. You can notice in below screenshot that I have duplicated creating object instance of each page inside my each test case.

Think of using web application with many pages and in that case this kind of code duplication is not a good practice. And also when number of pages grow up you need to import each one of them separately to the test runner file and it will be lengthy and unclear code you will be ended up with.

<div>
<div>//test.spec.js</div>
<div>const base = require('@playwright/test');</div>
<div>const { LoginPage } = require('../pages/LoginPage');</div>
<div>const { DashboardPage } = require('../pages/DashboardPage');</div>
<div>const testData = JSON.parse(JSON.stringify(require('../test-data/TestData.json')));</div>
<br />
<div>test.describe('E2E Test Suite', () => {</div>
<div> </div>
<div>    test.beforeEach(async ({ page }) => {</div>
<div>        const loginPage = new LoginPage(page);</div>
<div>        await loginPage.GotoLoginPage();</div>
<div>        await loginPage.Login(testData.credentials.username, testData.credentials.password);</div>
<div>    });</div>
<div> </div>
<div>    test('Validate Total Balance', async ({ page }) => {</div>
<div>        const dashboardPage = new DashboardPage(page);</div>
<div>        await dashboardPage.AssertTotalBalance(testData.financialDetails.totalBalance);</div>
<div>    });</div>
<div> </div>
<div>    test('Validate Credit Available', async ({ page }) => {</div>
<div>        const dashboardPage = new DashboardPage(page);</div>
<div>        await dashboardPage.AssertCreditAvailable(testData.financialDetails.creditAvailable);</div>
<div>    });</div>
<div> </div>
<div>    test('Validate Due Today', async ({ page }) => {</div>
<div>        const dashboardPage = new DashboardPage(page);</div>
<div>        await dashboardPage.AssertDueToday(testData.financialDetails.dueToday);</div>
<div>    });</div>
<div> </div>
<div>    test.afterAll(async ({ page }) => {</div>
<div>        await page.close();</div>
<div>    }); </div>
<div> </div>
<div>});</div>
</div>

Applying Fixtures 

In Playwright, a fixture is a set of preconditions or setup steps that are executed before running each test case. As a solution for above discussed code duplication we are going to define two fixtures :  loginPage and dashboardPage. We do this in separate file and lets call it as base.js.

Each fixture is an async function that takes a page object as input and returns a Page Object instance created using the LoginPage and DashboardPage classes.

//base.js
const base = require(‘@playwright/test’);
const { LoginPage } = require(‘../pages/LoginPage’);
const { DashboardPage } = require(‘../pages/DashboardPage’);

exports.test = base.test.extend({
    loginPage: async ({ page }, use) => {
        await use(new LoginPage(page));
    },
 
    dashboardPage: async ({ page }, use) => {
        await use(new DashboardPage(page));
    },
});
exports.expect = base.expect;
 
Now import base.js into your test runner and just mention fixture in test function argument as in below example. I hope now you understand that even if you creates new pages it is only base.js you have to modify to import this pages not the all test runner files and also you can pass relevant fixture to the relevant test function. 
 
//test.spec.js
const { test } = require(‘../base/base.js’);
const testData = JSON.parse(JSON.stringify(require(‘../test-data/TestData.json’)));

test.describe(‘E2E Test Suite’, () => {

    test.beforeEach(async ({ loginPage }) => {
        await loginPage.GotoLoginPage();
        await loginPage.Login(testData.credentials.username, testData.credentials.password);
    });

    test(‘Validate Total Balance’, async ({ dashboardPage }) => {
        await dashboardPage.AssertTotalBalance(testData.financialDetails.totalBalance);
    });

    test(‘Validate Credit Available’, async ({ dashboardPage }) => {
        await dashboardPage.AssertCreditAvailable(testData.financialDetails.creditAvailable);
    });

    test(‘Validate Due Today’, async ({ dashboardPage }) => {
        await dashboardPage.AssertDueToday(testData.financialDetails.dueToday);
    });

    test.afterAll(async ({ page }) => {
        await page.close();
    });
   
});

Using this approach, we can easily add new Page Objects and fixtures as needed, without having to initialize page objects in each test case repeatedly. When your automation repository grows up and when it is required to add more and more automated test cases it is a good practice to keep it in a state where modification is easy and less complex.

I’m sharing one of my github project where I tried above concepts in a real world example : https://github.com/Thara90/Playwright_POM

Soon we will meet with another tech discussion like this. If you have any suggestions or ideas please feel free to just leave a comment below.

Have a good day !!

About Author

Leave a Reply

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

11 − 4 =

Categories