Playwright · Reliable end-to-end tests

What are fixtures and how do you build a custom one?

Hardfixturestest.extendsetup teardownUpdated Oct 2026
Quiz this track

Short answer

Fixtures are named setup and teardown units injected into tests. test.extend defines custom fixtures, and Playwright runs teardown automatically even when a test fails.

Fixtures replace the beforeEach and afterEach pattern with typed, composable dependencies. A test asks for what it needs by name, and Playwright resolves the dependency graph, runs setup, hands over the value and runs teardown in reverse order.

ts
import { test as base } from "@playwright/test";
import { CartPage } from "./cart-page";

type Fixtures = {
  cartPage: CartPage;
};

export const test = base.extend<Fixtures>({
  cartPage: async ({ page }, use) => {
    const cartPage = new CartPage(page);
    await cartPage.open();
    await use(cartPage);
    await cartPage.clear();
  },
});
The fixture owns setup and cleanup, so the test only asks for cartPage.

Test scope versus worker scope

  • Test-scoped fixtures are created fresh for each test and are the default
  • Worker-scoped fixtures are shared by all tests in the same worker process
  • Built-in fixtures include page, context, browser, request and browserName
  • An auto fixture runs without being named in the test signature
  • Fixtures can depend on other fixtures, including built-in ones
ts
type WorkerFixtures = {
  accountId: string;
};

export const test = base.extend<{}, WorkerFixtures>({
  accountId: [async ({}, use) => {
    const id = await createAccount();
    await use(id);
    await deleteAccount(id);
  }, { scope: "worker" }],
});
A worker-scoped fixture is created once per worker and shared by its tests.

Worker scope is useful for expensive resources such as an authentication token or a seeded tenant, but it introduces shared state. If tests mutate the shared resource, they can affect each other even though browser contexts are isolated. Prefer test scope unless the setup cost is genuinely high.

Fixtures are not a place to hide assertions. They should create a valid starting condition, not verify application behaviour. If setup fails, the fixture should throw with a clear message so the cause is obvious.

Follow-ups you should expect

Interviewers use these to check whether the first answer was understanding or memorisation.

  1. 1What is the difference between test-scoped and worker-scoped fixtures?
  2. 2When does an auto fixture run?
  3. 3What happens to a fixture's teardown if the test fails?
  4. 4Why can worker-scoped fixtures cause order dependence?

Say it in the interview

Fixtures are typed setup and teardown units that tests receive by name. I create custom ones with the extend helper, and Playwright runs the teardown automatically even on failure. Test-scoped is the default, while worker-scoped fixtures are shared across a worker and are good for expensive resources. I use them to build a valid starting state, not to hide assertions or application checks.

Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.

Related questions

Reading is step one

Now say it out loud.

Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Playwright mock interview and get scored feedback on depth, clarity and confidence.