Fixtures are Playwright's dependency-injection mechanism for setting up and tearing down the environment a test needs (page, context, request, or your own custom fixtures). A test declares the fixtures it wants as function arguments and Playwright builds exactly those, in the right order, per test.
Built-in fixtures include page, context, browser, browserName and request. Playwright creates a fresh browser context and page for each test by default, which is why tests are isolated without manual cleanup. You can define custom fixtures to encapsulate setup — a logged-in page, seeded test data, a configured API client — so tests stay focused on behavior. Fixtures have scopes: 'test' (rebuilt per test) or 'worker' (shared across tests in a worker process, good for expensive one-time setup like a DB connection). Fixtures compose (one can depend on another) and run teardown after the yield/await, replacing scattered beforeEach/afterEach hooks with reusable, typed building blocks.
Twenty tests each need a logged-in admin. Instead of repeating login steps in every beforeEach, you define an adminPage fixture that logs in once (via storageState) and yields a ready page. Tests just declare async ({ adminPage }) and start asserting business logic — setup lives in one typed, reusable place.
// fixtures.ts — a custom authenticated-page fixture
import { test as base } from '@playwright/test';
type Fixtures = { adminPage: import('@playwright/test').Page };
export const test = base.extend<Fixtures>({
adminPage: async ({ browser }, use) => {
const context = await browser.newContext({ storageState: 'auth/admin.json' });
const page = await context.newPage();
await use(page); // hand the page to the test
await context.close(); // teardown after the test
},
});
// a test consuming it
test('admin can open settings', async ({ adminPage }) => {
await adminPage.goto('/settings');
await expect(adminPage.getByRole('heading', { name: 'Settings' })).toBeVisible();
});Scenario: Your suite repeats the same 8-line login in beforeEach across five spec files. It's slow and when the login flow changes you edit it in five places. How do fixtures solve this, and how do you avoid re-logging-in for every single test?