Fixtures

By Syncfusion®, Inc. · Last updated

What fixtures are and why they beat hooks, the built-in fixtures (page, context, browser, browserName, request), the testInfo object, custom fixtures with test.extend and use(), fixtures that depend on others, overriding built-ins, test and worker scope, automatic fixtures, timeouts, option fixtures, and sharing fixtures with mergeTests and types.

Fixtures

  • You have used { page } in every test. page is a fixture.
  • A fixture is something a test needs, prepared by Playwright before the test and cleaned up after it.
  • You can write your own fixtures, such as "a logged-in page" or "a cart with one item".
  • In this lesson, you will see how fixtures work and build several of your own.

Simple lesson: a fixture is ready-made setup that a test asks for by name.

A Real-World Example

  • Many of Priya's tests need a logged-in user.
  • Instead of copying the login steps into every test, she writes a loggedInPage fixture once.
  • Every test that asks for loggedInPage gets a page that is already logged in.
  • Open tests/fixtures.ts in the explorer to see the fixture. The test file imports test from it, not from @playwright/test.

What You Will Learn

  1. What fixtures are, and why they are better than hooks.
  2. The built-in fixtures.
  3. The testInfo object.
  4. How to write custom fixtures with setup and teardown.
  5. Test scope, worker scope, automatic fixtures and timeouts.
  6. Option fixtures you can set from config or test.use().
  7. How to share fixtures between files.

Fixture Fundamentals

What Are Fixtures?

  • A test asks for fixtures by name in its first argument: async ({ page, browserName }) => { ... }.
  • Playwright creates only the fixtures the test asks for, in the right order, and cleans them up afterwards.

Animation: a fixture runs its setup, then use hands the value to the test, the test runs, and after the test the fixture runs its teardown

fixture: loggedInPage

setup: log in await use(page) teardown: log out

test body uses loggedInPage

page

Code before use() is setup. Code after use() is teardown. use() waits while the test runs.

Setup, hand over with use(), wait for the test, then tear down.

Fixtures vs Hooks

Hooks (beforeEach)Fixtures
WhereIn each test fileIn one shared file
Runs forEvery test in the groupOnly tests that ask for it
Setup and teardownTwo separate hooksOne function, around use()
ReuseCopy and pasteImport and ask by name

Why Fixtures Are Preferred

  • Setup and teardown live together, so you never forget the clean-up.
  • A test lists what it needs right in its arguments. Reading the test tells you its setup.
  • Playwright builds fixtures only when needed, so unused setup costs nothing.

Simple lesson: fixtures keep setup and teardown together, and run only for tests that need them.

Built-in Fixtures

FixtureGives youScope
pageA new page (tab) for this testtest
contextThe browser context that page lives intest
browserThe shared browserworker
browserName'chromium', 'firefox' or 'webkit'worker
requestAn API client for HTTP requeststest
  • context is used in depth in Browser Contexts, Pages, and Emulation. request is used in API Testing.

Simple lesson: page, context, browser, browserName and request come built in.

The testInfo Object

test.info()

  • testInfo describes the running test. Get it as the second argument of a test, or with test.info().

Test Title, Project and Retry Count

  • testInfo.title, testInfo.project.name and testInfo.retry tell you which test, which project and which try this is.

testInfo.outputPath()

  • testInfo.outputPath('cart.json') gives a file path inside this test's own folder in test-results/.
  • testInfo.attach() adds a file or text to the report.
  • Open the Steps tab. The attachment is listed under the test.

Simple lesson: testInfo tells a test about itself, and lets it attach files to the report.

Custom Fixtures

test.extend()

  • base.extend({ ... }) makes a new test with extra fixtures. Each key is a fixture name.
  • Export the new test and import it in your test files instead of the original.

Setup and Teardown with use()

  • A fixture is an async function with three arguments: the fixtures it needs, use, and testInfo.
  • Everything before await use(value) is setup. Everything after it is teardown.

Fixtures That Depend on Other Fixtures

  • A fixture asks for other fixtures in its first argument, like a test does.
  • Playwright builds them in the right order: dependencies first.

Overriding Built-in Fixtures

  • You can redefine page. Ask for the original page and wrap it.
  • Below, every test's page starts on the products page, and a cart fixture depends on it.

Simple lesson: test.extend() adds fixtures. Setup goes before use(), teardown after it.

Fixture Scope and Behavior

Test-Scoped and Worker-Scoped Fixtures

  • Test scope (the default): a fresh value for every test.
  • Worker scope: one value shared by all tests in the same worker process. Use it for slow setup, such as creating a test account.
  • Write the scope in an options object: [async ({}, use) => { ... }, { scope: 'worker' }].

Animation: one worker runs three tests. A worker-scoped account is created once at the start and shared by all three tests. A test-scoped page is created fresh for each test and thrown away after it

Worker 1

account (scope: 'worker'): created once, shared by every test

A worker fixture lives as long as the worker. A test fixture is made and thrown away for every test.

Automatic Fixtures

  • { auto: true } makes a fixture run for every test, even if no test asks for it.
  • Good for things that must always happen, such as logging or checking for console errors.

Fixture Timeouts

  • A slow fixture can have its own timeout: { timeout: 60_000 }.
  • The Terminal shows "create the account once" a single time, for two tests.

A worker-scoped fixture is shared. If one test changes it, the next test sees the change. Keep shared values read-only.

Simple lesson: test scope gives each test its own value. Worker scope shares one. auto runs it always.

Fixture Options

Option Fixtures

  • An option is a fixture with a default value that tests and config can change.
  • Declare it with { option: true }: currency: ['INR', { option: true }].

Setting Option Values

  • Change it for a file or a group with test.use({ currency: 'USD' }).
  • Change it for a whole project in the config, under use.
  • Built-in settings such as baseURL, viewport and locale are option fixtures too.

Simple lesson: option fixtures have defaults you can change with test.use() or the config.

Sharing Fixtures

Putting Fixtures in a Shared File

  • Keep fixtures in files such as tests/fixtures.ts. Export test and expect from it.
  • Every test file imports from that file.

mergeTests()

  • Big projects split fixtures by topic: auth-fixtures.ts, cart-fixtures.ts.
  • mergeTests(authTest, cartTest) combines them into one test with all their fixtures.
import { mergeTests } from '@playwright/test';
import { test as authTest } from './auth-fixtures';
import { test as cartTest } from './cart-fixtures';

export const test = mergeTests(authTest, cartTest);

Typed Fixtures (TypeScript)

  • base.extend<MyFixtures>({ ... }) tells TypeScript each fixture's type.
  • Then the editor completes loggedInPage. with page methods, and marks typos.
  • The second type argument lists worker fixtures: base.extend<TestFixtures, WorkerFixtures>().

The first argument of a test or fixture must be an object pattern such as { page }. Playwright reads the names in it to know which fixtures to build. async (fixtures) => ... is an error.

Fixtures are where Page Object Model classes usually come from. A productsPage fixture can hand each test a ready-made page object.

Simple lesson: keep fixtures in a shared file, and combine several with mergeTests().

Try It in Your Project

  • These steps take the lesson from the lab to your own app.
  • Replace one beforeEach that creates data with a fixture that also cleans it up.
  • Put your fixtures in tests/fixtures.ts and import test from there in every spec.
  • Make one slow setup worker-scoped and measure the time saved.

Simple lesson: use one idea from this lesson on your own app this week.

TL;DR

  • A fixture is setup a test asks for by name. Playwright builds and cleans it up.
  • Built-ins: page, context, browser, browserName, request.
  • testInfo describes the running test and can attach files.
  • base.extend({ name: async (deps, use) => { setup; await use(value); teardown } }).
  • Scope is test (default) or worker. auto runs a fixture always. option makes it configurable.
  • Share with a fixtures file and mergeTests().

Common mistakes

MistakeWhat happensFix
async (fixtures) => ..."First argument must use the object destructuring pattern"Write async ({ page }) => ...
Forgetting await use(...)The test never gets the valueCall await use(value) exactly once
Importing test from @playwright/test in a spec that needs custom fixtures"Test has unknown parameter"Import test from your fixtures file
Teardown code before use()It runs before the test, not afterPut clean-up after await use(...)
Changing a worker-scoped value in a testOther tests see the changeTreat worker fixtures as read-only

Check Yourself

Exercises

What to Remember

  • Fixtures are named setup that tests ask for.
  • Setup before use(), teardown after it.
  • Test scope by default. Worker scope for slow, shared, read-only setup.
  • auto fixtures always run. option fixtures are set with test.use() or the config.
  • Share fixtures through one file, and mergeTests() for several.

Next: Playwright Configuration