skip to content

What does Playwright Test do with a fixture that a test never lists in its arguments?

level: middleimportance: should knowfreq 44%

answer

  1. Setup is driven by names
  2. Dependencies come along for the ride
  3. Unused fixture costs nothing
  4. Automatic ones are the exception
  5. Side effects need the name present

basics

~20 s

Nothing. Playwright Test sets up fixtures on demand, so a fixture no test names is never built for that test. A test that asks only for request never causes a browser context or page to be created.

solid answer

~50 s

Fixture setup is **driven by the argument list**, not by a fixed pipeline. The runner inspects the names a test, or a hook, destructures and builds exactly those, plus whatever they transitively depend on, then tears them down in reverse afterwards. Naming `page` therefore pulls in `context` and the worker's `browser` behind it, while a test written as `async ({ request }) => { ... }` gets an isolated `APIRequestContext` and no page or context at all. Fixtures declared as automatic are the deliberate exception, since they run whether or not anyone names them. The practical consequences are that an unused fixture costs nothing, that mixed suites of UI and API tests do not pay browser setup on the API ones, and that a fixture whose side effect you actually want must be named or made automatic.

code

typescript · 13 lines
typescript
import { test, expect } from '@playwright/test';

// Names only request: no context and no page are ever built for this test.
test('the roles endpoint answers', async ({ request }) => {
  const response = await request.get('https://admin.example.com/api/roles');
  expect(response.ok()).toBeTruthy();
});

// Names page: context and the worker's browser are pulled in behind it.
test('the roles screen lists them', async ({ page }) => {
  await page.goto('https://admin.example.com/roles');
  await expect(page.getByRole('listitem')).toHaveCount(3);
});

go deeper

for a junior

Take away the rule: if you do not name a fixture, it is not created. A test with an empty argument list opens no browser page at all.

for a middle

Explain the dependency graph and the ordering, that naming page pulls in context and browser, and that teardown runs in reverse of setup.

for a senior

Use it when tuning a suite: keep signatures honest so API-level cases never pay for a context, and audit side-effect fixtures that quietly stopped being named.

for a principal

Treat on-demand setup as a cost lever across the whole suite, and set the convention for when a cross-cutting fixture becomes automatic instead of relying on every author to name it.

## Setup follows the argument list Playwright Test does not run a fixed sequence of preparations before every test. It reads the property names you destructure and constructs **only those**: ```typescript test('a', async ({ page }) => { /* context and browser are built */ }); test('b', async ({ request }) => { /* no page, no context */ }); test('c', async () => { /* nothing at all is built */ }); ``` Test `c` runs with no browser page whatsoever. That is not an optimisation you switched on; it is the default resolution rule. ## Dependencies are pulled in, not pushed Fixtures form a graph, and naming one drags in its ancestors: - naming `page` requires `context`, which requires `browser`; - naming `context` requires `browser`, but no page is opened; - naming `request` requires none of the three, since it is an independent test-scoped fixture; - naming `browserName` gives you a string with no browser page attached. Setup runs in dependency order before the body and teardown in the reverse order after it, so nothing is torn down while something that depends on it is still alive. ## The exception worth knowing Fixtures declared as automatic are the deliberate escape hatch: they run for every test in their scope whether or not anyone names them, which is how cross-cutting setup gets applied without editing every signature. That mechanism belongs to the custom-fixture and worker-scope topics; what matters here is knowing that "only what you name" has exactly one documented exception, so an interviewer's follow-up does not catch you flat. ## Why it matters in a real suite Take an internal admin console with several staff roles and a suite that mixes UI journeys with checks that only call the service directly: 1. The UI tests name `page` and pay for a context each. 2. The service-level tests name `request` and never start a page. 3. Both kinds run under the same runner, in the same files if you like, with no flag to separate them. | Test signature | Browser used | Context built | Page opened | |---|---|---|---| | `async ({ page })` | yes | yes | yes | | `async ({ context })` | yes | yes | no | | `async ({ request })` | no | no | no | | `async ()` | no | no | no | The saving is not theoretical: on a suite where a meaningful share of cases never touch the DOM, skipping context creation for them removes real work from every run. ## The failure mode to recognise Because setup is name-driven, a fixture with a **side effect** you depend on does nothing if no one names it. Someone writes a fixture that seeds a staff account, removes it from a test's signature during a refactor because "the test does not use the value", and the seeding silently stops happening. The test then fails for a reason that looks unrelated to the edit. The fix is either to keep the name in the signature or to declare the fixture automatic so it no longer depends on being mentioned. ## Answering it well State the rule, then the two corollaries: only named fixtures and their dependencies are set up; therefore unused fixtures are free, and therefore a fixture you rely on for its side effect must be named or automatic. Mentioning that a `request`-only test never opens a browser page is a concrete, checkable example that shows you have actually seen the behaviour rather than read about it.

  • A fixture that seeds data stopped running after a refactor. What is the likely cause?
    Its name was dropped from the test's destructured arguments. Setup is name-driven, so an unnamed fixture is simply not built and its side effect never happens. Either restore the name or declare the fixture automatic so it no longer depends on being mentioned.
  • Does naming context instead of page still open a page?
    No. You get a fresh `BrowserContext` and no page in it, so you would open one yourself if you needed it. The dependency runs upward toward the browser, never downward into extra pages.

saying these in an interview costs you the question

  • Says all built-in fixtures are set up every test
  • Thinks an unused fixture still costs setup time
  • Expects a side-effect fixture to run unnamed
  • Believes naming context also opens a page
  • Claims every test starts a browser regardless