Resource library

QA Interview

Playwright Fixtures Interview Questions for TypeScript Testers (2026)

Practice playwright fixtures interview questions typescript testers face, with typed examples covering scope, dependencies, teardown, overrides, and parallelism

24 min read | 3,394 words

TL;DR

Strong fixture answers explain setup, dependency resolution, scope, teardown, typing, and parallel safety. Interviewers want to hear why a fixture owns a resource lifecycle and what tradeoff follows from test-scoped versus worker-scoped reuse.

Key Takeaways

  • Describe a fixture as lifecycle-managed dependency injection, not merely shared setup.
  • Choose test scope for mutable state and worker scope only for expensive, safely shareable resources.
  • Type test fixtures and worker options separately with the second generic parameter to test.extend.
  • Place cleanup after use and make teardown idempotent so failures do not leak data.
  • Use automatic fixtures for cross-cutting evidence, not hidden business behavior.
  • Keep fixtures focused on ownership while page objects model UI behavior.
  • Prove fixture designs are parallel-safe under retries, multiple workers, and project overrides.

Candidates searching for playwright fixtures interview questions typescript guidance need more than syntax. A strong answer shows that you can model dependencies, select the correct lifetime, guarantee cleanup, preserve type safety, and keep parallel workers isolated.

This interview hub contains 50 focused questions with answers you can say aloud and code you can run. For broader framework context, pair it with the Playwright TypeScript framework guide, then practice adapting each design to a real product constraint.

TL;DR

Topic Interview-ready point
Fixture meaning A named dependency with runner-managed setup, use, and teardown
Test scope One instance per test, safest for mutable users, pages, and records
Worker scope One instance per worker process, useful for expensive shareable services
Dependency Request another fixture in the setup function parameter
Cleanup Put idempotent teardown after await use(value)
TypeScript Declare fixture value types and worker option types explicitly
Overrides Replace a fixture or option through test.extend or project use
Parallel safety Avoid shared mutable accounts, files, ports, and database rows

The short mental model is setup -> use -> teardown. The runner resolves only the fixtures a test needs, orders dependencies, and unwinds teardown in reverse dependency order.

1. Playwright Fixtures Interview Questions TypeScript Fundamentals

Q: What is a fixture in Playwright Test?

A fixture is a value whose creation and disposal are controlled by the Playwright Test runner. The setup function calls use with that value, and code after use performs teardown. Unlike a plain beforeEach, a fixture is named, typed, composable, and initialized only when the test or another fixture requests it.

Q: Why does Playwright call fixtures dependency injection?

The test declares required values by destructuring names such as { page, account }, and the runner supplies them. A custom fixture can request other fixtures in exactly the same way, producing a dependency graph. This removes manual construction from the spec while keeping dependencies visible in its signature.

Q: What happens around await use(value)?

Statements before use are setup, the awaited call transfers control to the test and its downstream dependents, and statements after it are teardown. The fixture must await use; otherwise its setup function can finish before the test consumes the resource. Explain this boundary whenever an interviewer asks how cleanup works.

Q: How are fixtures different from hooks?

Hooks run because of suite placement, while fixtures run because a test requests a dependency or the fixture is marked automatic. Fixtures compose through typed parameters and can have test or worker scope. Hooks remain useful for suite-level reporting or behavior that truly applies to every test, but they do not express a reusable value and its owner as clearly.

Q: Which fixtures are built into Playwright Test?

Common built-ins include browser, context, page, request, browserName, and test options such as baseURL. The page belongs to an isolated context created for the test, while browser is shared more broadly by the runner. A good candidate understands these lifetimes before adding custom wrappers.

2. Creating Typed Custom Fixtures

Q: How do you create a custom fixture in TypeScript?

Define its value type, call base.extend<Fixtures>(), and export the resulting test plus Playwright's expect. The following file is runnable when the application serves /health under the configured base URL. It demonstrates a small dependency with no unnecessary class.

// fixtures.ts
import { test as base, expect, type APIRequestContext } from '@playwright/test';

type HealthClient = { status: () => Promise<number> };
type AppFixtures = { healthClient: HealthClient };

export const test = base.extend<AppFixtures>({
  healthClient: async ({ request }: { request: APIRequestContext }, use) => {
    await use({
      status: async () => (await request.get('/health')).status(),
    });
  },
});

export { expect };
// health.spec.ts
import { test, expect } from './fixtures';

test('health endpoint responds', async ({ healthClient }) => {
  expect(await healthClient.status()).toBe(200);
});

Run npx playwright test health.spec.ts. The fixture type appears in editor completion, and a misspelled fixture name fails TypeScript compilation.

Q: Why export test from the fixture module?

The extended test carries both runtime fixture registration and compile-time types. Importing the original test from @playwright/test bypasses the custom fixture and makes its name unavailable. Teams often prevent this mistake with a consistent local import convention.

Q: Should a fixture value be a class or an object?

Use the shape that communicates responsibility most clearly. A class suits a stateful client or page object with cohesive methods, while a small object is easier for one or two operations. The fixture owns lifetime either way, so inheritance is not required.

Q: Can fixture types be inferred?

TypeScript can infer local expressions, but test.extend<AppFixtures> should state the public fixture contract explicitly. That catches a setup function passing the wrong value to use and gives stable autocomplete to specs. See typing Playwright fixtures for more advanced generic patterns.

Q: How do you avoid unsafe type assertions in fixture code?

Model optional configuration honestly, validate environment input at startup, and return an object that satisfies the declared interface. Do not use as unknown as Client to silence an incomplete mock. A compile-time clean fixture can still be wrong at runtime, so validate required URLs and credentials with actionable errors.

3. Scope and Lifecycle Decisions

Q: What is the default scope of a custom fixture?

The default is test scope. Setup runs once for each test that needs the fixture, and teardown finishes after that test. This is the conservative choice for mutable data because retries and neighboring tests receive fresh instances.

Q: When should you use worker scope?

Use worker scope for expensive resources that can safely serve several tests in one worker, such as a read-only service client or uniquely allocated account pool. The fixture runs once per worker process, not once for the entire suite. If a worker restarts after failure, its worker fixture is created again.

Q: Is a worker-scoped page a good optimization?

Usually no, because pages accumulate cookies, routes, dialogs, and application state across tests. Sharing one also undermines Playwright's isolation model and makes order matter. Optimize login with storage state or API setup while retaining a fresh context and page per test.

Q: How does retry behavior affect fixture lifecycle?

A retried test runs in a new worker after the failed worker is discarded. Test fixtures are recreated, and worker fixtures in the replacement process are also set up again. Therefore external resources need unique identifiers and teardown that tolerates partial setup or repeated deletion.

Q: How do scope choices affect performance?

Test scope spends setup cost for stronger isolation; worker scope amortizes that cost but increases sharing risk. Measure the real setup duration before changing scope, then audit every mutable property and server-side side effect. A faster suite with cross-test contamination is a poor trade.

4. Fixture Dependencies and Execution Order

Q: How does one fixture depend on another?

Request the dependency in the first parameter of the fixture setup function. The runner initializes the dependency first, passes its value to the dependent fixture, and tears the dependent down before its prerequisite. Names matter because fixture resolution follows registered keys.

Q: In what order are independent fixtures initialized?

Do not depend on incidental declaration order. Only requested fixtures are set up, and dependency edges determine required ordering. If order is functionally necessary, express it as a dependency rather than relying on object property placement.

Q: What is lazy fixture initialization?

A non-automatic fixture is created only when a test, hook, or another active fixture requests it. This can save expensive work for tests that do not need the resource. The fixture box lazy initialization tutorial explores patterns for delaying construction inside a fixture value too.

Q: Can fixture dependencies form a cycle?

No useful dependency graph can contain a -> b -> a, because neither setup can begin first. Break the cycle by extracting the shared prerequisite into a third fixture or by narrowing responsibilities. Cycles often reveal that two abstractions own the same lifecycle.

Q: How should teardown order work for nested resources?

If order depends on customer, delete the order before deleting the customer. Playwright achieves this naturally by unwinding dependent fixtures before their dependencies. Keep cleanup with the fixture that created the resource so the graph represents both construction and destruction constraints.

5. Options, Overrides, and Projects

Q: What is the difference between a fixture and a test option?

Both use the fixture system, but an option is configuration intended for use in config or test.use. Declare it with { option: true } and normally supply a default value. Options should be serializable, understandable inputs such as a tenant name, not live browser objects.

Q: How do you type worker fixtures and worker options?

Pass test-scoped types as the first generic and worker-scoped types as the second. Tuple notation configures scope and option metadata. This example creates one label per worker and permits project-level region selection.

import { test as base, expect } from '@playwright/test';

type TestFixtures = { runLabel: string };
type WorkerFixtures = { region: string; workerLabel: string };

export const test = base.extend<TestFixtures, WorkerFixtures>({
  region: ['us-east', { scope: 'worker', option: true }],
  workerLabel: [async ({ region }, use, workerInfo) => {
    await use(`${region}-w${workerInfo.workerIndex}`);
  }, { scope: 'worker' }],
  runLabel: async ({ workerLabel }, use, testInfo) => {
    await use(`${workerLabel}-${testInfo.retry}-${testInfo.title}`);
  },
});

test('label is unique enough for test data', async ({ runLabel }) => {
  expect(runLabel).toContain('us-east-w');
});

Run npx playwright test. A project can override region through use: { region: 'eu-west' } while preserving its declared worker lifetime.

Q: How do you override a built-in fixture?

Extend test and register the same key, then request upstream dependencies needed to construct the replacement. A common example overrides page to navigate to a starting route before each test. Keep such behavior explicit because every consumer inherits it.

Q: When is test.use() appropriate?

Use it to override options or fixtures for a file or describe block, such as locale, permissions, or a typed tenant option. Avoid changing values in a way that surprises readers several nested blocks later. For reusable policies, named projects are often easier to discover.

Q: What can go wrong when overriding a fixture?

An override can hide navigation, create a resource at the wrong scope, or discard behavior supplied by the base fixture. Verify that dependent fixtures still receive the intended value and that teardown remains intact. Review Playwright fixture override examples for focused practice.

6. Teardown and Failure Handling

Q: Does fixture teardown run when a test assertion fails?

Yes, control returns from await use(value) and the remaining fixture code runs during teardown. This is a primary advantage over cleanup placed at the end of a test body. Process termination can still prevent cleanup, so persistent environments also need stale-resource collection.

Q: What does idempotent cleanup mean?

Deleting the same resource twice or deleting an already absent resource should not produce a misleading failure. Treat a not-found response as successful cleanup when appropriate and scope identifiers to the current test. Idempotency matters when setup partly fails, a retry starts, or an external janitor runs.

Q: Should teardown errors fail the test?

A teardown failure must remain visible because it can poison later runs or consume infrastructure. Preserve the original assertion evidence while attaching or reporting cleanup details separately when possible. Do not swallow every error with an empty catch block.

Q: How do you clean up if setup fails before use?

Track each resource immediately after creation and wrap later setup in try/finally when partial construction is possible. If the fixture never reaches use, code placed only after use may not cover every intermediate failure path. Design creation APIs to return identifiers early and deletion APIs to tolerate incomplete state.

Q: Where should database cleanup live?

Place record-specific cleanup in the fixture that creates those records, using an API or repository boundary approved for tests. Avoid broad table truncation in a parallel shared environment. Prefix data with a run identifier and use a scheduled janitor as defense in depth, not as the primary lifecycle.

7. Authentication, Pages, and Domain Objects

Q: Should login be implemented as a fixture?

A fixture is appropriate when it owns an authenticated context, storage state, or provisioned identity used by multiple tests. UI login itself should remain in dedicated authentication coverage rather than silently running before every scenario. Most tests can load validated storage state or authenticate through a supported API.

Q: How do fixtures and page objects differ?

A fixture manages when a value is created and destroyed; a page object models locators and domain interactions on a page. A fixture may construct a page object with the current page, but the page object should not decide worker scope or delete accounts. Separating lifetime from behavior keeps both abstractions focused.

Q: How would you provide two logged-in users to one test?

Create a test-scoped fixture that provisions two distinct identities and two browser contexts, each loaded with the correct storage state. Pass a typed object such as { adminPage, memberPage } to the test and close both contexts after use. Never reuse the same mutable account for both roles merely to reduce setup.

Q: Is storage state itself enough for test isolation?

No. Separate contexts isolate browser cookies and local storage, but two contexts using the same account still share server-side carts, preferences, and records. Allocate accounts by test or worker according to mutation patterns and reset server state deliberately.

Q: Where should assertions live when a fixture returns a page object?

Keep business expectations in the spec so reviewers can see the outcome, while allowing page objects to expose locators or domain observations. A fixture may assert setup preconditions, such as successful account creation, because failure there means the dependency is invalid. Avoid hiding the test's central claim inside fixture setup.

8. Parallelism and Worker Fixtures

Q: What does worker scope mean under parallel execution?

Each worker process receives its own instance, and tests assigned to that worker reuse it. It is not a singleton across all machines, shards, or projects. Resource names should include worker and run identity when collisions are possible.

Q: Which worker index should you use for unique data?

workerInfo.workerIndex is unique among workers launched during the run, including replacement workers, while parallelIndex identifies the parallel slot. For external account allocation that must remain stable across a restarted slot, parallelIndex can be useful. Still include a CI run identifier so two simultaneous pipelines cannot collide.

Q: How do worker fixtures behave with sharding?

Every shard runs separate worker processes and therefore separate worker fixture instances. A bare index such as w0 repeats across shards and machines. Combine shard or build identity, project name, and worker identity when naming remote resources.

Q: What resources are unsafe to share in a worker fixture?

A mutable page, a single shopping account, a fixed download path, or an unpartitioned database row can leak state between tests. Read-only clients and immutable reference data are safer. The decisive question is whether one test can alter what another test observes.

Q: How do you design multi-user worker fixtures?

Allocate a distinct account pool per worker, then lease separate users to tests without concurrent reuse. Reset mutable server state before returning an account to the pool, or prefer test-scoped users when reset is unreliable. The worker fixtures for multi-user testing provides a complete reference design.

9. Automatic Fixtures, Metadata, and Debugging

Q: What is an automatic fixture?

An automatic fixture uses { auto: true }, so it runs even when the test does not list its name. It fits cross-cutting concerns such as attaching logs after failure or starting scoped diagnostics. Because the dependency is invisible in the test signature, use automatic behavior sparingly.

Q: How can a fixture attach debug evidence?

Accept testInfo, collect sanitized logs or resource identifiers, and call testInfo.attach with a body and content type. Attach after use when the test status is known, optionally only for failures. Never attach secrets, access tokens, or unredacted customer data.

Q: What are fixture titles useful for?

A configured fixture title can make reports and setup steps easier to read. Boxed fixtures can reduce report noise by grouping internal setup. Reporting options improve presentation, but they should not compensate for an oversized fixture doing unrelated work.

Q: How do you diagnose a fixture timeout?

Determine whether time is spent before use, inside the test, or after use, then inspect setup API calls and teardown waits. Give an expensive fixture a justified fixture-specific timeout rather than inflating every test timeout. Log safe phase boundaries so a stalled dependency is distinguishable from a slow assertion.

Q: Why might a fixture never run?

A lazy fixture runs only when requested, unless it is automatic. Check that the spec imports the extended test, the fixture name matches, and no shadowed base import bypasses registration. Also verify that a dependent fixture actually destructures it in its setup parameters.

10. Advanced Playwright Fixtures Interview Questions TypeScript Design

Q: How do you merge fixture modules?

Use Playwright's supported mergeTests helper when separately extended test objects need to become one test API. Confirm that fixture names do not collide and that their scope assumptions are compatible. Prefer a small number of domain-focused modules over a single registry that imports the entire organization.

Q: How do fixtures interact with project dependencies?

A setup project runs tests before dependent projects, while fixtures manage values inside a worker or test lifecycle. Use a setup project when the setup deserves its own test result and trace, such as creating reusable authentication state. Do not expect an in-memory fixture value to cross project process boundaries.

Q: Should API clients be fixtures?

A typed API client is a good fixture when it needs credentials, base URL configuration, or cleanup coordination. Decide whether its underlying request context is test-scoped based on mutable headers and session state. Keep endpoint methods honest about responses rather than converting every failure into a generic Boolean.

Q: How do you test fixture code itself?

Create small consumer specs that assert observable setup and teardown effects, including failure and retry cases. Run with multiple workers and repeated execution to expose collisions. Type checking validates the contract, while integration tests validate lifetime and external cleanup.

Q: When is a fixture abstraction too large?

It is too large when it provisions unrelated domains, hides the primary test journey, or forces most specs to pay for unused setup. Split it along lifecycle ownership and allow dependencies to compose smaller values. A fixture should make a requirement clearer, not turn every test into { app }.

11. How Interviewers Grade Your Answers

Interviewers listen for a causal explanation, not a memorized definition. Start with the resource being owned, name the suitable scope, explain dependencies, and finish with cleanup and parallel behavior. For a worker fixture, explicitly say what happens on retries and worker restart. For authentication, distinguish browser isolation from shared server-side state.

Answer level What it sounds like
Weak Fixtures remove duplicate code.
Developing Use test.extend and call use.
Strong This test-scoped fixture creates a unique order through the request context, yields its typed ID, then deletes it idempotently after the test.
Senior Test scope protects mutable orders; if setup cost becomes material, I would measure it before pooling accounts per worker and prove reset behavior under retries and shards.

In coding rounds, import from @playwright/test, declare types, await use, and show complete teardown. In design rounds, challenge unsafe sharing and mention observability. You can rehearse further with Playwright coding interview questions and then use /practice for timed answers.

12. Common Mistakes

  • Calling every helper a fixture even when it has no lifecycle or injected dependency.
  • Importing the base test in a spec that needs the extended fixture set.
  • Forgetting await use(value), which breaks the intended setup and teardown boundary.
  • Making a page or mutable account worker-scoped to save a few seconds.
  • Assuming separate browser contexts isolate server-side account data.
  • Relying on fixture declaration order instead of declaring dependencies.
  • Catching and discarding teardown errors, leaving polluted environments.
  • Using automatic fixtures for hidden navigation or business setup.
  • Declaring an option without { option: true } and then expecting project use semantics.
  • Naming external resources with only a worker index, which collides across shards and builds.
  • Putting assertions for the main business outcome inside a fixture.
  • Growing one universal fixture that constructs every page object and service client.

Conclusion

Playwright fixtures interview questions TypeScript testers answer well are grounded in ownership. Identify the resource, expose a typed value, choose the narrowest safe scope, express dependencies, and guarantee teardown. Then test the design against retries, worker restarts, shards, and shared server state.

Practice writing one test-scoped data fixture and one worker-scoped service fixture from memory. Run them with multiple workers, force a failure, and confirm cleanup. When you can explain every lifecycle event and tradeoff, your answer will sound like production experience rather than API recall.

Interview Questions and Answers

What is a fixture in Playwright Test?

A fixture is a runner-managed dependency with setup, a typed value supplied through `use`, and teardown. Tests request fixtures by destructured name. This makes lifecycle and dependencies explicit compared with global setup helpers.

What happens before and after await use in a fixture?

Code before `await use(value)` creates or prepares the resource. The awaited call covers execution of the test and downstream consumers. Code after it disposes the resource.

When would you choose a worker-scoped fixture?

I choose worker scope only for expensive resources that tests in one worker can share safely. I account for worker restart, retries, shards, and parallel pipelines when assigning external identifiers. Mutable pages and shared customer accounts normally remain test-scoped.

How do you type test and worker fixtures separately?

I pass test-scoped fixture types as the first generic to `base.extend` and worker-scoped fixture or option types as the second. Worker registrations use tuple syntax with `{ scope: 'worker' }`. This lets TypeScript verify scope-aware dependencies and consumer values.

How does Playwright determine fixture setup order?

It resolves the dependency graph created by fixture parameters and sets prerequisites up before dependents. Lazy fixtures that nobody requests do not run unless automatic. Teardown reverses the dependency order.

How do you make fixture cleanup reliable?

I put cleanup after `await use`, make deletion idempotent, and preserve resource identifiers as soon as creation succeeds. For partial setup, I use targeted `try/finally` handling. I also keep a stale-data janitor for abrupt process failures.

What is the risk of sharing authentication state?

Storage state isolates browser sessions only when loaded into separate contexts. If tests use the same server-side account, carts, preferences, and records can still race. I allocate identities according to mutation boundaries, often per test or per worker.

When should you use an automatic fixture?

I reserve automatic fixtures for cross-cutting capabilities that genuinely apply to every test, such as sanitized failure attachments. They hide dependencies from test signatures, so I avoid using them for navigation or data setup.

How are fixtures different from page objects?

Fixtures control creation, injection, scope, and disposal. Page objects encapsulate locators and domain behavior. A fixture may provide a page object, but it should not blur lifecycle ownership with UI assertions.

How would you debug a fixture that never runs?

I confirm the spec imports the extended `test`, check the registered name, and verify that the test or a dependency requests it. Because ordinary fixtures initialize lazily, an unused fixture correctly does nothing. I also inspect whether a base test import shadowed the custom export.

How do fixture overrides work?

An extended test can register the same fixture key with new setup, and options can be changed through project or local `use` configuration. I verify that dependent fixtures receive the replacement and that its scope and teardown contract remain compatible.

How do you validate that a fixture design is parallel-safe?

I run consumer specs repeatedly with multiple workers, retries, and shards, then inspect resource names and cleanup. I look for shared mutable accounts, fixed paths, ports, and database keys. Passing once in serial mode is not evidence of isolation.

Frequently Asked Questions

What are Playwright fixtures in TypeScript?

Playwright fixtures are typed values whose setup, injection, and teardown are managed by the test runner. You define them with `test.extend`, pass the value through `use`, and request them by name in a test or another fixture.

What is the difference between test-scoped and worker-scoped fixtures?

A test-scoped fixture is created separately for every test that requests it. A worker-scoped fixture is created once per worker process and reused by tests assigned to that worker, so it must be safe to share.

Does Playwright fixture teardown run after a failed test?

Yes, code after `await use(value)` runs when control returns to the fixture, including after an assertion failure. External cleanup should still be idempotent because abrupt process termination can interrupt teardown.

Can a Playwright fixture depend on another fixture?

Yes. Destructure the dependency in the fixture setup function's first parameter. Playwright initializes prerequisites before dependents and tears dependents down first.

Should page objects be Playwright fixtures?

A fixture can construct and inject a page object when many tests need it. Keep responsibilities separate: the fixture owns lifetime, while the page object owns locators and domain interactions.

When should I use an automatic Playwright fixture?

Use `{ auto: true }` for truly cross-cutting behavior such as collecting failure diagnostics. Avoid automatic fixtures for hidden navigation or business setup because tests no longer declare that dependency visibly.

How should I prepare for Playwright fixture interview questions?

Practice writing typed test and worker fixtures, dependencies, overrides, options, and reliable teardown. Run examples with retries and multiple workers, then explain the isolation and performance tradeoffs aloud.

Related Guides