What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use test.beforeEach when setup must run before every test. Import test from @playwright/test, register an asynchronous callback, and request fixtures such as page in the callback argument. A hook declared at file scope applies to every test in that file; a hook inside test.describe applies only to that group.
Basic beforeEach syntax
This complete example opens the same page before each test. Because navigation is asynchronous, the callback and page.goto call both use async and await.
import { test, expect } from '@playwright/test';
test.beforeEach(async ({ page }) => {
await page.goto('https://playwright.dev/');
});
test('has the expected title', async ({ page }) => {
await expect(page).toHaveTitle(/Playwright/);
});
test('shows the get started link', async ({ page }) => {
await expect(page.getByRole('link', { name: 'Get started' })).toBeVisible();
});
The first parameter is an object containing Playwright Test fixtures. Destructuring { page } asks the runner for the test’s isolated page. You can request other available fixtures in the same way.
Choosing the hook’s scope
File-level setup
Declare the hook outside any test.describe callback when every test in the file needs the same preparation:
#1 Best Overall
test.beforeEach(async ({ page }) => {
await page.goto('/dashboard');
});
test('shows account details', async ({ page }) => { /* ... */ });
test('opens billing settings', async ({ page }) => { /* ... */ });
This registration affects all tests that follow in the file, including tests not logically related to the dashboard. Keep file-level hooks narrowly focused so unrelated tests do not pay for unnecessary setup.
Group-level setup with test.describe
Put the registration inside a describe block to limit it to that group:
test.describe('authenticated dashboard', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill(process.env.TEST_USER_EMAIL!);
await page.getByLabel('Password').fill(process.env.TEST_USER_PASSWORD!);
await page.getByRole('button', { name: 'Sign in' }).click();
await page.goto('/dashboard');
});
test('shows the account name', async ({ page }) => {
await expect(page.getByTestId('account-name')).toBeVisible();
});
});
test('public home page remains unauthenticated', async ({ page }) => {
await page.goto('/');
});
The login hook runs only for tests inside authenticated dashboard. Prefer this arrangement when public and authenticated scenarios share one file.
Ordering and multiple hooks
Playwright runs every applicable beforeEach hook before a test, in registration order. A file-level hook runs before a group-level hook registered later in that group. This lets you separate broad preparation from group-specific preparation, but too many layers can make failures difficult to trace.
Recommended Free Tools
test.beforeEach(async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 800 });
});
test.describe('reports', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/reports');
});
test('lists recent reports', async ({ page }) => { /* ... */ });
});
If one applicable hook fails, Playwright continues running the other applicable hooks. The test will still be reported as failed, and later hook actions may produce secondary errors. Keep hooks independent where possible and make failure messages identify the setup step.
Naming a hook
An optional title improves reports and error output:
Rank #2
test.beforeEach('Open the reports page', async ({ page }) => {
await page.goto('/reports');
});
Using fixtures inside beforeEach
Fixtures are the normal way to obtain Playwright-managed resources. The built-in page fixture is created for the test, made available to the hook and test, and torn down after the test completes. Browser instances are shared across tests in a worker, while each test receives an isolated browser context.
test.beforeEach(async ({ page, context }) => {
await context.addCookies([
{
name: 'feature',
value: 'new-navigation',
domain: 'localhost',
path: '/',
},
]);
await page.goto('http://localhost:3000');
});
Request only the fixtures you actually need. Manual browser creation inside a hook usually duplicates the runner’s lifecycle management and can leak resources.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsAccessing TestInfo
The callback can also receive an optional TestInfo argument. It is useful for diagnostics or for adapting setup to the current test’s metadata:
import { test } from '@playwright/test';
test.beforeEach(async ({ page }, testInfo) => {
await page.goto('/');
console.log(`Preparing ${testInfo.title} in ${testInfo.project.name}`);
});
beforeEach versus beforeAll
beforeEach runs once per test. Use it when every test needs a fresh, repeatable state. beforeAll runs once before all tests in a file or describe group (per worker process), so it is appropriate for expensive shared preparation that is safe to reuse.
| Hook | Runs | Typical use | Important trade-off |
|---|---|---|---|
beforeEach |
Before every test | Navigate, authenticate, seed isolated state | Repeated cost, but strong test isolation |
beforeAll |
Once per file or group and worker | Start a shared service or create reusable data | Shared state can create test coupling |
Do not replace per-test isolation with shared mutable data merely to make a suite faster. If setup is expensive, a fixture can often preserve isolation while creating the resource only when a test requests it.
When a fixture is better than a hook
A hook is ideal for simple, local actions such as opening a URL or logging in. Define a fixture instead when setup should be reused across files, composed with other fixtures, created only by tests that need it, or paired with teardown in one definition.
Rank #3
Reusable fixture with teardown
import { test as base } from '@playwright/test';
type TestFixtures = {
projectId: string;
};
export const test = base.extend<TestFixtures>({
projectId: async ({ request }, use) => {
const response = await request.post('/api/projects', {
data: { name: 'fixture-project' },
});
const project = await response.json();
await use(project.id);
await request.delete(`/api/projects/${project.id}`);
},
});
The code before use creates the resource, the test receives the value, and the code after use performs teardown. This keeps lifetime management next to the resource definition instead of splitting creation across beforeEach and cleanup across afterEach.
Automatic per-test setup
For global per-test behavior, create an automatic test-scoped fixture in a custom fixture module and import that module in tests. The fixture then runs before each test without repeating a hook declaration in every file. This is useful for cross-cutting setup such as collecting diagnostics or establishing a common server state.
Timeouts and slow setup
Time spent in beforeEach counts toward the test timeout shared with the test body. A slow navigation, login, or data seed can therefore cause a timeout before assertions begin. First fix unnecessary work; then adjust the timeout deliberately.
test.beforeEach(async ({ page }, testInfo) => {
test.setTimeout(testInfo.timeout + 30_000);
await page.goto('/slow-report');
});
The exact effective timeout also depends on the installed Playwright version and your project configuration. Keep timeout changes local to the slow scenario when possible, rather than making every test wait longer.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Common mistakes and fixes
The hook affects the wrong tests
Symptom: public tests unexpectedly perform login or navigate to an unrelated page. Fix: move the hook into the relevant test.describe block, or split the file by behavior.
Navigation finishes after the test starts
Symptom: assertions run against the old page or fail intermittently. Fix: mark the callback async and await every asynchronous action, especially page.goto, clicks that trigger navigation, API requests, and waits.
Authentication state leaks between tests
Symptom: tests pass or fail depending on execution order. Fix: use Playwright’s isolated context and fixtures, avoid mutable module-level variables, and create test data with unique identifiers.
Setup is duplicated across many files
Symptom: changing login or seed logic requires edits everywhere. Fix: move the behavior into a custom fixture. Import the extended test object in each file so the same lifecycle and teardown rules apply.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchA hook failure hides the useful error
Symptom: the report contains several cascading errors. Fix: give hooks descriptive titles, split large hooks into focused fixtures, and log the current test title or project through TestInfo.
Using beforeEach for once-only work
Symptom: a costly seed or server startup repeats for every test. Fix: evaluate beforeAll or a worker-scoped fixture, while ensuring shared state cannot let one test affect another.
Keeping hooks fast and reliable
- Navigate directly to the page under test instead of clicking through several screens unless the journey itself is what you are testing.
- Prefer API-based data setup or a fixture over long UI setup for every test.
- Use stable locators such as roles, labels, and explicit test IDs.
- Do not add arbitrary sleeps; wait for a selector, response, or state that represents readiness.
- Keep each hook responsible for one coherent precondition and give expensive operations a measured timeout.
- Run tests in isolation when diagnosing order-dependent failures, then restore parallel execution after fixing the shared-state problem.
Or skip the browser setup
If your goal is to capture a page image rather than exercise it interactively, ScreenshotNeo provides a single screenshot request without maintaining Playwright browser setup.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for all request options. Before capture, it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan. Create a free ScreenshotNeo account to start.
FAQ
Can a beforeEach callback have a title?
Yes. Pass a string before the callback, for example test.beforeEach('Prepare checkout', async ({ page }) => { ... }). The title appears in reports and setup errors.
Does a hook run before retries as well as the first attempt?
Each test attempt has its own setup phase, so per-test preparation is rerun when Playwright retries a failed test. Design setup to be repeatable and avoid assuming that a previous attempt left data behind.
Can I call test.beforeEach inside a test?
No. Register hooks while Playwright is collecting the test file and describe blocks. Calling a hook from a test body is too late and does not define predictable per-test setup.
What should I do if setup needs cleanup?
Use a fixture when creation and teardown belong together. An afterEach hook can work for simple cases, but fixtures make ownership, reuse, and lifetime explicit.
Frequently Asked Questions
Can a `beforeEach` callback have a title?
Yes. Pass a string before the callback; the title appears in reports and setup errors.
Does setup run again on a retry?
Each retry has its own setup phase, so the hook runs again. Make setup repeatable.
Can I register `beforeEach` inside a test body?
No. Register hooks while the test file and describe blocks are being collected.
How should setup that needs cleanup be implemented?
Prefer a fixture when creation and teardown belong together; use an afterEach hook only for simple local cleanup.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




