Fixtures & Test Organisation
Use fixtures to compose reusable setup, isolate test state, and apply the Page Object Model without over-engineering.
Fixtures & Test Organisation video demo
A practical screen-share style walkthrough for chapter 3, showing how the Playwright idea works in TypeScript and Java.
Briefing focus
Fixtures — the composition primitive
This is a structured lesson briefing. Real video/audio can be added later as a media source.
Estimated time
10 min
- 1Fixtures — the composition primitive
- 2Page Object Model — used well
Transcript brief
Use fixtures to compose reusable setup, isolate test state, and apply the Page Object Model without over-engineering. The video brief explains the mental model first, then demonstrates the workflow using the course code samples, and finishes with reliability checks you can apply in CI.
Key takeaways
- Translate the concept into a maintainable Playwright test.
- Understand the TypeScript and Java equivalents without changing the test intent.
- Avoid the common source of flaky or slow end-to-end tests for this topic.
Fixtures — the composition primitive
A fixture is a named resource with setup and teardown. Playwright's built-in fixtures — page, context, browser, request — are already injected into every test. You compose your own on top.
// tests/fixtures.ts
import { test as base, expect } from '@playwright/test';
type Fixtures = {
signedInPage: import('@playwright/test').Page;
};
export const test = base.extend<Fixtures>({
signedInPage: async ({ page }, use) => {
await page.goto('/sign-in');
await page.getByLabel('Email').fill(process.env.TEST_EMAIL!);
await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!);
await page.getByRole('button', { name: /sign in/i }).click();
await expect(page.getByRole('heading', { name: /dashboard/i })).toBeVisible();
await use(page);
// no teardown needed — context is disposed automatically
},
});
export { expect };Page Object Model — used well
POM is not about wrapping every selector in a class. Use it to encapsulate flows that change — sign-in, checkout, filtering — and leave trivial pages as inline locators.
// pages/sign-in.page.ts
import { type Page, expect } from '@playwright/test';
export class SignInPage {
constructor(private page: Page) {}
async signIn(email: string, password: string) {
await this.page.goto('/sign-in');
await this.page.getByLabel('Email').fill(email);
await this.page.getByLabel('Password').fill(password);
await this.page.getByRole('button', { name: /sign in/i }).click();
await expect(this.page).toHaveURL(/\/dashboard/);
}
}Structure by feature, not by page
Organise folders as tests/checkout/, tests/auth/, etc. It makes it obvious which feature owns which tests — essential when the suite passes 500 tests.