Skip to content

Quick Guide to Playwright Fixtures: Enhancing Your Tests

Quick Guide to Playwright Fixtures: Enhancing Your Tests

Introduction

Following our recent blog post on migrating E2E tests from Cypress to Playwright, we've identified opportunities to enhance our test scripts further. In this guide, we'll delve into the basics of Playwright fixtures, demonstrating their utility and flexibility in test environments.

Playwright fixtures are reusable components that set up and tear down the environment or conditions necessary for tests. They are crucial for writing clean, maintainable, and scalable tests. Fixtures can handle tasks like opening a browser, initializing a database, or logging into an application—actions you might need before running your tests.

As a practical example, we'll revisit one of the tests from our previous post, enhancing it with a new fixture to streamline the testing process and significantly improve maintainability. This post is designed to provide the foundational skills to integrate fixtures into your testing workflows effectively, giving you the confidence to manage and maintain your test scripts more efficiently.

Creating Our First Fixture

To illustrate the power of fixtures in Playwright, let’s consider a practical example from a test scenario in our project. Below is a snippet of a test case from our newsletter page:

test.describe("Newsletter page", () => {
  test("subscribing to email newsletter should show success message", async ({ page }) => {
    await page.goto("/newsletter");
    await page
      .locator(`[data-testid="newsletter-email-input"]`)
      .fill(SIGN_UP_EMAIL_TEST);
    await page.locator(`[data-testid="newsletter-submit-button"]`).click();
    await expect(
      page.getByText(/You subscribed to the newsletter successfully!/).first(),
    ).toBeVisible();
  });
});

This test scenario navigates to the newsletter page, fills in an email, submits the form, and checks for a success message. To optimize our test suite, we'll refactor common actions like navigation, form completion, and submission into reusable fixtures. This approach makes our tests cleaner and more maintainable and reduces redundancy across similar test scenarios.

Implementing the Fixture

Here’s how the fixture looks:

// NewsletterPage.fixture.ts
import type { Page, Locator } from "@playwright/test";

export class NewsletterPage {
  private readonly emailInput: Locator;
  private readonly submitButton: Locator;

  constructor(public readonly page: Page) {
    this.emailInput = this.page.locator(
      `[data-testid="newsletter-email-input"]`,
    );
    this.submitButton = this.page.locator(
      `[data-testid="newsletter-submit-button"]`,
    );
  }

  async goto() {
    await this.page.goto("/newsletter");
  }

  async addEmail(text: string) {
    await this.emailInput.fill(text);
  }

  async submitNewsletter() {
    await this.submitButton.click();
  }
}

This fixture encapsulates the actions of navigating to the page, filling out the email field, and submitting the form. By abstracting these actions, we simplify and focus our test cases.

Refactoring the Test

With the fixture in place, let’s see how it changes our original test file:

import { NewsletterPage } from "playwright/fixtures/NewsletterPage.fixture";

test.describe("Newsletter page", () => {
  let newsletterPage: NewsletterPage;

  test.beforeEach(({ page }) => {
    newsletterPage = new NewsletterPage(page);
  });

  test("subscribing to email newsletter should show success message", async ({ page }) => {
    await newsletterPage.goto();
    await newsletterPage.addEmail(SIGN_UP_EMAIL_TEST);
    await newsletterPage.submitNewsletter();
    await expect(
      page.getByText(/You subscribed to the newsletter successfully!/).first(),
    ).toBeVisible();
  });
});

A beforeEach method to reset the state of the NewsletterPage fixture ensures a clean and consistent environment for each test scenario. This practice is crucial for maintaining the integrity and reliability of your tests.

By leveraging the NewsletterPage fixture, each test within the "Newsletter page" suite starts with a clean and pre-configured environment. This setup improves test clarity and efficiency and aligns with best practices for scalable test architecture.

Conclusion

As we've seen, fixtures are powerful tools that help standardize test environments, reduce code redundancy, and ensure that each test operates in a clean state. By abstracting common setup and teardown tasks into fixtures, we can focus our testing efforts on what matters most, verifying the behavior and reliability of the software we're developing.

Remember, the key to successful test management is choosing the right tools and using them wisely to create scalable, maintainable, and robust testing frameworks. Playwright fixtures offer a pathway towards achieving these goals, empowering teams to build better software faster and more confidently.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co