Playwright Fixtures: Custom Fixtures Guide
Step-by-step guide on creating custom Playwright fixtures. Expose page objects, mock data, and environments across your test files.
playwright-v1-49-matrix
Playwright Fixtures: Custom Fixtures Guide
Fixtures establish clean environments for test executions. They encapsulate page objects, login states, and mock databases. This guide explains how to define and use custom fixtures in your test suite.
Introduction
Traditional testing frameworks rely on beforeEach and afterAll hooks to manage test environments. This pattern creates execution dependency and duplication. Playwright resolves this by introducing component fixtures.
Fixtures list dependencies explicitly. The runner analyzes these declarations, builds the environment dynamically, and cleans up assets automatically on completion.
The Playwright Fixtures Model
Playwright fixtures are lazy-loaded. The runner only compiles and boots environments requested by the test arguments:
graph TD
TestRun["Start Test: test('name', ({ todoPage }) => ...)"] --> CheckDeps["Check: Does todoPage exist in registry?"]
CheckDeps --> SetupFixture["Run Setup: page.goto('/todo') and mock APIs"]
SetupFixture --> InjectTest["Inject todoPage instance into Test Function"]
InjectTest --> RunTestBody["Execute Test Body Assertions"]
RunTestBody --> TeardownFixture["Run Teardown: Reset cache & cookies"]
TeardownFixture --> TestComplete["Test Completes Cleanly"]This model reduces setup execution overhead.
Fixture Initialization Sequence
A custom fixture execution matches the following runtime sequence:
sequenceDiagram
participant Runner as Test Runner
participant Fixture as Custom Fixture (todoPage)
participant Page as Browser Page
Runner->>Fixture: Request fixture instance
Fixture->>Page: Allocate fresh page context
Fixture->>Page: Navigate to url & perform actions
Fixture-->>Runner: Yield page instance to test
Runner->>Runner: Execute test assertions
Runner->>Fixture: Terminate test (post-yield)
Fixture->>Page: Close page context and clean cache
Fixture-->>Runner: Return teardown completeImplementation Steps
Follow these steps to construct a custom page object fixture.
1. Define the Page Object Class
Create the page helper class pages/TodoPage.ts to encapsulate page actions:
import { Page, Locator } from '@playwright/test';
export class TodoPage {
readonly page: Page;
readonly todoInput: Locator;
readonly todoItems: Locator;
constructor(page: Page) {
this.page = page;
this.todoInput = page.getByPlaceholder('What needs to be done?');
this.todoItems = page.getByRole('listitem');
}
async goto() {
await this.page.goto('/todo');
}
async addTodo(text: string) {
await this.todoInput.fill(text);
await this.todoInput.press('Enter');
}
}2. Extend expect and test Settings
Create the extended test runner file fixtures/todoFixture.ts to register the class:
import { test as base } from '@playwright/test';
import { TodoPage } from '../pages/TodoPage';
// Declare custom fixture interfaces
type MyFixtures = {
todoPage: TodoPage;
};
// Extend base test configuration with custom page fixture
export const test = base.extend<MyFixtures>({
todoPage: async ({ page }, use) => {
// 1. Setup phase: initialize class and navigate
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addTodo('Standard Task');
// 2. Yield fixture instance to test execution
await use(todoPage);
// 3. Teardown phase: execute cleaning scripts
await page.evaluate(() => localStorage.clear());
},
});
export { expect } from '@playwright/test';3. Implement in Test Files
Import your custom extended test script and use the fixture:
import { test, expect } from '../fixtures/todoFixture';
test('should add custom task items', async ({ todoPage }) => {
// Use pre-populated fixture instance
await todoPage.addTodo('Buy milk');
// Assert item rendering
await expect(todoPage.todoItems).toContainText(['Standard Task', 'Buy milk']);
});Fixture Scope Comparison
The table below contrasts the execution life cycle scopes available in Playwright.
| Scope Level | Initialization Rate | Teardown Trigger | Recommended Use Case |
|---|---|---|---|
| Test Scope | Once per test file run | After individual test file exits | Default page object setups |
| Worker Scope | Once per worker process | On worker termination | Database configurations, browser contexts |
| Global Scope | Once per test execution run | After all tests finish | Server spin-ups, global state builds |
Best Practices for Custom Environments
Here are a few key practices:
use() statement to guarantee resource releases.Common Mistakes to Avoid
use() yield hook. This blocks test runners.| Bad Pattern | Recommended Alternative |
Nested beforeEach setups across files | Define a common extended test fixture |
| Modifying shared worker variables | Keep test states encapsulated in test-scoped files |
Forgetting to call use() | Always yield the initialized instance |
Frequently Asked Questions
What happens if I forget to export custom fixtures?
The runner fails to resolve references, throwing undefined property errors during test suite parsing.
Can I override standard fixtures like page?
Yes. Extend the base runner and define custom logic under the page property key to override default settings.
What is the difference between fixtures and hooks?
Hooks run statically on every test file. Fixtures load dynamically, executing setups only when requested by test arguments.
Can I use multiple custom fixtures in a single test?
Yes. Declare the arguments list (e.g. { todoPage, apiHelper }) to run setup routines for both modules.
How do I configure worker-scoped fixtures?
Specify the scope parameter in the declaration block (e.g. { scope: 'worker' }).
Are custom fixtures reusable across browser projects?
Yes. The extension configurations execute across all projects (Chromium, WebKit, Firefox) defined in the main configuration.
How do I access browser credentials inside fixtures?
Expose saved credentials files within the setup properties and import them inside context definitions.
Can I use page object models inside fixtures?
Yes. Page object initialization is the primary use case for custom test fixtures.
How do I pass configurations to worker fixtures?
Inject environment variables or read global configuration parameters during worker setup.
Does teardown execute on test failures?
Yes. Playwright guarantees that statements written after the use() line run even if tests crash.
Summary
Custom fixtures improve test speed and readability. Structuring environments in a modular format simplifies test suite maintenance.
Related Articles
About The Author
PlaywrightPad Editorial reports on Chromium engines, E2E test optimizations, and AI integration specifications.
Newsletter
Get weekly browser reports sent directly to your inbox.