Saturday, May 24, 2025

How to combine POMs (Page Object Models) with Playwright Fixtures for better developer experience

Playwright fixtures and traditional Page Object Models (POM) are both used in test automation, but they serve different purposes and can be integrated for better results. Playwright fixtures help set up and manage test environments, while POM organizes page interactions. 

In Playwright, you can use fixtures to handle POM instances, making tests cleaner and more maintainable.

How to combine POMs (Page Object Models) with Playwright Fixtures for better developer experience


Traditional POM Approach

Page Object Model (POM) is a design pattern where you create separate classes for each page and manually create objects in each test.

loginPage.js

class LoginPage {
    constructor(page) {
        this.page = page;
        this.usernameInput = "#username";
        this.passwordInput = "#password";
        this.loginButton = "#login";
    }

    async goToLoginPage() {
        await this.page.goto("https://example.com/login");
    }

    async enterUsername(username) {
        await this.page.fill(this.usernameInput, username);
    }

    async enterPassword(password) {
        await this.page.fill(this.passwordInput, password);
    }

    async clickLoginButton() {
        await this.page.click(this.loginButton);
    }
}

export { LoginPage };


login.spec.js

import { test, expect } from "@playwright/test";
import { LoginPage } from "../pages/loginPage";

test("Login Test", async ({ page }) => {
    const loginPage = new LoginPage(page);
    
    await loginPage.goToLoginPage();
    await loginPage.enterUsername("testuser");
    await loginPage.enterPassword("password");
    await loginPage.clickLoginButton();
    
    expect(await page.url()).toBe("https://example.com/home");
});


Problems in the Traditional POM Approach

  1. Code duplication : You have to create new LoginPage(page) in every test.
  2. Hard to maintain : If LoginPage changes, all test files using it must be updated.
  3. Longer test setup : Every test has repeated setup code (new LoginPage(page)).


Playwright Fixture Approach

Playwright fixtures are built-in mechanisms to manage reusable objects (like LoginPage). Instead of creating objects manually, fixtures inject them automatically.

Let's see the code below where we have loginPage class, we fixture class, and finally, the test class where we execute our test cases.

loginPage.js

class LoginPage {
    constructor(page) {
        this.page = page;
        this.usernameInput = "#username";
        this.passwordInput = "#password";
        this.loginButton = "#login";
    }

    async goToLoginPage() {
        await this.page.goto("https://example.com/login");
    }

    async enterUsername(username) {
        await this.page.fill(this.usernameInput, username);
    }

    async enterPassword(password) {
        await this.page.fill(this.passwordInput, password);
    }

    async clickLoginButton() {
        await this.page.click(this.loginButton);
    }
}

export { LoginPage };


fixture.js

import { test as base } from "@playwright/test";
import { LoginPage } from "../pages/loginPage";

export const test = base.extend({
    loginPage: async ({ page }, use) => {
        const loginPage = new LoginPage(page);
        await use(loginPage);
    }
});

export { expect } from "@playwright/test";

login.spec.js

import { test, expect } from "../utils/fixture";

test("Login Test", async ({ loginPage }) => {
    await loginPage.goToLoginPage();
    await loginPage.enterUsername("testuser");
    await loginPage.enterPassword("password");
    await loginPage.clickLoginButton();
    
    expect(await loginPage.page.url()).toBe("https://example.com/home");
});


Benefits of the Fixture-Based Approach

Using fixtures (like in your code) removes duplication and makes tests cleaner.

1. No Need to Create Objects in Every Test

Instead of const loginPage = new LoginPage(page); in every test, we automatically inject loginPage using Playwright fixtures.

2. Improved Readability and Maintainability

If the LoginPage constructor changes, we only update it in one place (fixtures).

All tests automatically use the updated version.

3. Faster Test Execution (Fixture Reuse)

Playwright reuses fixtures across tests when possible.

If multiple tests use the same page (e.g loginPage), Playwright does not create a new object every time, which speeds up execution.


When to Use Fixtures vs. POM?


Final Recommendation

  1. If you have just a few tests, using the traditional POM might be okay.
  2. If you have many tests and want better maintainability, fixtures might be the best choice.


This is all about implementing combine POMs (Page Object Models) with Playwright Fixtures for better developer experience.


No comments:

Post a Comment