Answer
Playwright Test Hooks
Playwright provides four lifecycle hooks for setting up and tearing down test state.
beforeEach — Runs Before Every Test
TypeScript
import { test, expect } from '@playwright/test';
test.beforeEach(async ({ page }) => {
// Runs before each test in this file
await page.goto('/login');
await page.getByLabel('Email').fill('user@test.com');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL('/dashboard');
});
test('can see dashboard', async ({ page }) => {
await expect(page.getByRole('heading')).toHaveText('Dashboard');
});
test('can view profile', async ({ page }) => {
await page.getByRole('link', { name: 'Profile' }).click();
await expect(page).toHaveURL('/profile');
});
afterEach — Runs After Every Test
TypeScript
test.afterEach(async ({ page }, testInfo) => {
if (testInfo.status !== testInfo.expectedStatus) {
// Take screenshot on failure
const screenshot = await page.screenshot();
await testInfo.attach('screenshot', { body: screenshot, contentType: 'image/png' });
}
});
beforeAll — Runs Once Before All Tests in the Suite
TypeScript
import { test, expect, Browser } from '@playwright/test';
test.describe('Admin Suite', () => {
let adminPage: any;
test.beforeAll(async ({ browser }) => {
// Create an admin session once for all tests
const context = await browser.newContext();
adminPage = await context.newPage();
await adminPage.goto('/admin/login');
await adminPage.getByLabel('Email').fill('admin@test.com');
await adminPage.getByLabel('Password').fill('admin123');
await adminPage.getByRole('button', { name: 'Sign in' }).click();
});
test.afterAll(async () => {
await adminPage.close();
});
test('admin can create user', async () => {
await adminPage.getByRole('link', { name: 'Users' }).click();
// ...
});
});
afterAll — Runs Once After All Tests
TypeScript
test.afterAll(async () => {
// Cleanup: delete test data, close DB connections, etc.
console.log('All tests done — cleaning up');
});
Hook Execution Order
CODE
beforeAll
└── beforeEach → test 1 → afterEach
└── beforeEach → test 2 → afterEach
└── beforeEach → test 3 → afterEach
afterAll
Hooks Inside describe Blocks
TypeScript
test.describe('Shopping Cart', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/cart');
});
test('shows empty cart message', async ({ page }) => {
await expect(page.getByText('Your cart is empty')).toBeVisible();
});
});
Note:
beforeAllin Playwright runs in the worker scope — avoid sharing mutable page state across tests, as tests may run in different workers. UsebeforeEachfor page-level setup.
