Advanced Playwright Fixtures: Worker-Scoped, Parameterized, and Auto-Cleanup Patterns
Basic fixtures inject page objects. Advanced fixtures manage database connections, share auth across workers, parameterize browser configs, and auto-cleanup test data — all with TypeScript type safety.
🎭 Want to master this with real projects? Join the Playwright Automation Mastery course at The Testing Academy.
Contents
import { test as base } from '@playwright/test';
// Worker-scoped: created once per worker, shared across all tests in that worker
export const test = base.extend<{}, { dbConnection: any }>({
dbConnection: [async ({}, use) => {
const db = await connectToDatabase();
await use(db);
await db.close(); // Cleanup after all tests in worker
}, { scope: 'worker' }],
});
Parameterized Fixtures
// Run same tests across multiple configs
export const test = base.extend<{ userRole: string }>({
userRole: ['admin', { option: true }],
});
// playwright.config.ts
projects: [
{ name: 'admin', use: { userRole: 'admin' } },
{ name: 'viewer', use: { userRole: 'viewer' } },
]
🚀 Level Up Your Playwright
From locators to CI pipelines — build a production-grade Playwright + TypeScript framework step by step.
Auto-Cleanup Fixture
export const test = base.extend<{ testUser: User }>({
testUser: async ({ request }, use) => {
// Setup: create via API
const res = await request.post('/api/users', {
data: { name: faker.person.fullName(), email: faker.internet.email() }
});
const user = await res.json();
await use(user); // Test runs here
// Teardown: guaranteed cleanup even if test fails
await request.delete('/api/users/' + user.id);
},
});
Fixture Composition
// Fixtures can depend on other fixtures
export const test = base.extend<{
api: ApiHelper;
testUser: User;
authenticatedPage: Page;
}>({
api: async ({ request }, use) => {
await use(new ApiHelper(request));
},
testUser: async ({ api }, use) => {
const user = await api.createUser();
await use(user);
await api.deleteUser(user.id);
},
authenticatedPage: async ({ page, testUser }, use) => {
await page.goto('/login');
await page.getByLabel('Email').fill(testUser.email);
await page.getByLabel('Password').fill(testUser.password);
await page.getByRole('button', { name: 'Login' }).click();
await use(page);
},
});
When to Use Each Scope
| Scope | Lifetime | Use For |
|---|---|---|
| test | Per test | Page objects, test data, API client |
| worker | Per worker process | DB connections, expensive setup, shared tokens |
🎓 Master Playwright End to End
Join hundreds of SDETs building real automation frameworks. Lifetime access, hands-on projects, and a job-ready portfolio.
