DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
automated testing

How to Use `beforeEach` in Playwright Tests

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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:

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Accessing 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A 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.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.