In Playwright, what does declaring a fixture { auto: true } change about when it runs?
answer
- Removes the condition, not the lifetime
- No test needs to name it
- Combines with either scope
- Made for side effects, not values
- Hidden dependency is the price
basics
~20 sNormally a Playwright fixture is set up only if a test lists it in its arguments. With auto true it is set up for every test in its scope regardless, so it runs even though no test mentions it.
solid answer
~50 sPlaywright sets a fixture up lazily: only tests that name it, directly or through another fixture, trigger it. `{ auto: true }` in the fixture's options object removes that condition, so the fixture runs for every test in its scope even when nothing references it. It changes the trigger, not the lifetime -- combined with `{ scope: 'worker' }` it runs once per worker before that worker's first test, and combined with the default test scope it runs around every test. That makes it the tool for fixtures that exist for a side effect rather than a value: starting a stub service, installing logging, checking after each test that no console errors were produced. The cost is a dependency no test signature shows, always paid, and a failure that takes out every test in the scope.
code
typescript · 12 linesimport { test as base } from '@playwright/test';
export const test = base.extend<{}, { stubIdp: void }>({
stubIdp: [
async ({}, use, workerInfo) => {
const idp = await startStubIdp(9100 + workerInfo.parallelIndex);
await use();
await idp.close();
},
{ scope: 'worker', auto: true },
],
});go deeper
Remember the one-line effect: without auto a fixture runs only when a test asks for it by name, and with auto it runs for every test in its scope anyway.
Explain that auto changes the trigger while scope changes the lifetime, and that the two combine, so auto plus worker scope means once per worker whether or not anyone asks.
Argue the tradeoff. Auto suits invisible setup such as instrumentation or a shared stub process, but it hides a dependency and widens failures to every test in the scope.
Set the convention for the suite: which side effects are allowed to be implicit, how they are documented, and what evidence justifies making every test pay for one.
By default, Playwright is lazy about fixtures: a fixture is only set up if the test names it in its destructured argument list, or if another fixture the test names depends on it. Nobody asks, nothing runs. `{ auto: true }` is the switch that removes that condition. ## What auto changes Adding `auto: true` to a fixture's options object makes Playwright set the fixture up **for every test in its scope, whether or not any test lists it**. It changes the trigger, not the lifetime -- scope still decides how long the value lives, and the two options combine freely. | Declaration | Runs when | Runs how often | |---|---|---| | plain function | a test names it | once per such test | | `{ auto: true }` | always | once per test | | `{ scope: 'worker' }` | a test in the worker names it | once per worker | | `{ auto: true, scope: 'worker' }` | always | once per worker | The last row is the "boot this once per worker no matter what" switch, and it is the reason `auto` appears so often next to worker scope. Without `auto`, a worker-scoped fixture that no test happens to name is simply never built -- which is usually a bug when the fixture exists for its side effect rather than its value. ## Value versus side effect The distinction that makes `auto` click is what the fixture is *for*. - A fixture that produces a **value** a test uses -- a client, a page object, a snapshot -- does not need `auto`. The test names it precisely because it wants the value. - A fixture that exists for its **side effect** -- starting a stub server, installing request logging, seeding a cache, asserting afterwards that no console errors appeared -- has no value the test would ever mention, so without `auto` it would never run. An `auto` fixture can still be listed by name if a test does want the value; `auto` only guarantees the setup happens, it does not hide the fixture. ## The worker-scoped case, in an admin console suite Consider a suite for an internal admin console with several staff roles. Booting a stub identity provider that every test's login flow talks to is expensive and has no per-test result. Declared `[fn, { scope: 'worker', auto: true }]`, it is started once in each worker before that worker's first test, stays up for every test the worker runs, and is shut down when the worker exits. No test mentions it and every test benefits. 1. The worker starts and picks up its first test. 2. Because the fixture is auto, its setup runs before that test's body -- no test had to ask. 3. Every later test in the same worker reuses the running instance; setup does not repeat. 4. The worker finishes its last test, and only then does the fixture's teardown run. ## What it costs `auto` is convenient exactly because it is invisible, and that is also the objection to it. - **The dependency is hidden.** A reader of a test file cannot see that the fixture exists, so a failure inside its setup arrives from nowhere. - **It is always paid.** Every test in the scope carries the cost, including the ones that would not have needed it. At worker scope the bill is once per worker, which is why the auto plus worker combination is usually the affordable one and auto plus test scope is the one to justify. - **It cannot be opted out of per test.** A test that would rather not have the side effect has no way to say so from inside the test. - **Failures are wide.** Because it runs for everything, a broken auto fixture fails every test in its scope rather than one. The practical policy is to reserve `auto` for setup that is genuinely universal and genuinely invisible -- instrumentation, a process every test depends on indirectly -- and to keep anything a test actually reads as an ordinary named fixture, so the dependency stays legible in the test signature.
- Can a test still read the value of an auto fixture?Yes. `auto` only guarantees the setup happens; it does not make the fixture private. A test that wants the value lists it in its destructured arguments exactly as it would any other fixture, and gets the instance that was already created for it. The flag is about the trigger, not about visibility.
- When would you make an auto fixture test-scoped rather than worker-scoped?When the side effect has to happen around every individual test rather than once per process: attaching a per-test console-error listener, resetting request routing, or asserting after the test that nothing was logged as an error. Worker scope is right only when the side effect is a one-off boot whose result every test in the worker can share safely.
- What is the argument against reaching for auto by default?It hides the dependency. Nothing in a test's signature reveals that the fixture ran, so a failure in its setup looks like it came from nowhere, and every test in the scope pays for it whether it benefits or not. Fixtures that produce a value a test reads should stay named, so the test file keeps documenting what it depends on.
saying these in an interview costs you the question
- Thinks auto true also changes the fixture's scope
- Says an auto fixture cannot be named by a test
- Believes auto only works with worker scope
- Uses auto for value fixtures and hides real dependencies
- Assumes an individual test can opt out of an auto fixture