Hooks such as beforeEach and afterEach, combined with test.describe groups, let you structure tests around shared context while still keeping individual cases clear. Used with fixtures, they give fine-grained control over setup and teardown.
Using Hooks and Test Groups
You can group related tests with test.describe and define hooks that run before or after each test in the group. This is useful for setting up common navigation or lightweight state.
// hooks-and-groups.spec.ts
import { test, expect } from '@playwright/test';
-test.describe('profile settings', () => {
+test.describe('profile settings', () => {
test.beforeEach(async ({ page }) => {
await page.goto('https://demo.myshop.com/profile');
});
test('can update display name', async ({ page }) => {
await page.getByRole('textbox', { name: 'Display name' }).fill('New Name');
await page.getByRole('button', { name: 'Save changes' }).click();
await expect(page.getByText('Profile updated')).toBeVisible();
});
test('can change password', async ({ page }) => {
await page.getByLabel('Current password').fill('OldPass123');
await page.getByLabel('New password').fill('NewPass456');
await page.getByRole('button', { name: 'Change password' }).click();
await expect(page.getByText('Password changed')).toBeVisible();
});
});
beforeEach and afterEach.Hooks also exist at the file level (top-level describe) or can be scoped to nested groups, so be explicit about where you define them and what they apply to.
Common Mistakes
Mistake 1 โ Doing heavy work in beforeEach hooks
This can slow tests dramatically.
โ Wrong: Starting new browser instances or seeding databases in every beforeEach.
โ Correct: Reserve heavy setup for higher-scope fixtures and keep hooks lean.
Mistake 2 โ Overusing nested describe blocks
This confuses readers.
โ Wrong: Deeply nested structures where it is unclear which hooks apply to which tests.
โ Correct: Prefer a small number of clear groups with well-named hooks.