Typorithm · medium

11. Fixtures, cleanup and parallel isolation

qatestingplaywrightindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

11. Fixtures, cleanup and parallel isolation

0%

Concept and contract

Make setup and teardown explicit without deleting another test’s data.

A fixture describes a reusable test dependency with a lifecycle. It can provide a unique namespace, an authenticated context or a local resource and clean it up afterward. The abstraction is useful when it makes ownership clearer, not when it hides important preconditions behind a large base class.

Test-scoped data should not depend on a previous test’s success. A suite that passes only in one order has an undeclared dependency. Keep parallelism bounded and let each test own the records it creates.

Mechanism and worked example

The lab’s support fixture supplies a unique x-qa-namespace header.

The lab’s support fixture supplies a unique x-qa-namespace header. The worked test saves one id, deletes only that namespace and verifies an empty saved list. The user cookie remains the same, while the server data for that test scope is reset.

This cleanup route exists only in the loopback teaching server. It must not be copied into a public production API. In a real system, use a reviewed disposable database or an appropriately protected test-data mechanism.

Complete worked example

typescript

import { test, expect } from "../support/test.js";
test("cleanup resets only its test namespace", async ({request}) => {
  await request.post("/api/login", {data:{user:"alice"}});
  await request.put("/api/saved/r1");
  expect(await (await request.get("/api/saved")).json()).toEqual({ids:["r1"]});
  await request.delete("/__test/namespace");
  expect(await (await request.get("/api/saved")).json()).toEqual({ids:[]});
});
Reasoning
The reset targets one synthetic namespace and is not a production cleanup mechanism.

Counterexamples and common mistakes

The weak teardown clears every account’s saved state from a shared dataset.

The weak teardown clears every account’s saved state from a shared dataset. A parallel worker can lose its fixture between action and assertion. Calling the deletion afterEach does not make its scope correct.

Avoid hiding flaky order dependence with retries. First identify which mutable resource is shared and give it an explicit owner. Sometimes a test must run serially, but that should be a documented resource constraint rather than the first repair for all failures.

Counterexample: what this check misses

typescript

// Weak teardown: clear every account or namespace after each test.
// Another worker may still be using those records.
Reasoning
Global cleanup can invalidate another worker’s preconditions.

Independent practice: small task

Create two API request contexts with separate namespace headers and the same fictional account.

Create two API request contexts with separate namespace headers and the same fictional account. Save r1 in one and r2 in the other, then reset only the first namespace. Verify the second still has r2.

Begin with both independent successful reads before adding cleanup. Optional extension: run the tests with two workers and reversed order. The basic task should demonstrate one clear isolation boundary.

Step 1: Read both namespace states successfully. Step 2: Reset one and verify the other is unchanged.

Practice starter: solve before reading the solution

typescript

import { test, expect } from "../support/test.js";
test("cleanup leaves another namespace intact", async ({playwright,baseURL,space}) => {
  // TODO: create two owned request contexts and reset only one.
});
Reasoning
Establish both independent states before resetting one.
  • Hint 1: Keep cleanup local and owned.
  • Hint 2: Retries do not repair shared mutable state.

Solution, tests and trade-offs

The solution makes both namespaces explicit and closes the request contexts in finally.

The solution makes both namespaces explicit and closes the request contexts in finally. Resetting the first cannot affect the second under the lab’s contract. This tests cleanup scope rather than merely trusting a helper named isolated.

The verifier runs normal examples with two workers. A separate domain mutation that shares saved state is rejected. Passing parallel execution once is still not proof that every possible shared resource has been identified.

Practice solution and acceptance checks

typescript

import { test, expect } from "../support/test.js";
test("cleanup leaves another namespace intact", async ({playwright,baseURL,space}) => {
  if(!baseURL) throw new Error("Configured lab URL required");
  const a = await playwright.request.newContext({baseURL,extraHTTPHeaders:{"x-qa-namespace":space+"-a"}});
  const b = await playwright.request.newContext({baseURL,extraHTTPHeaders:{"x-qa-namespace":space+"-b"}});
  try {
    await a.post("/api/login", {data:{user:"alice"}}); await b.post("/api/login", {data:{user:"alice"}});
    await a.put("/api/saved/r1"); await b.put("/api/saved/r2");
    await a.delete("/__test/namespace");
    expect(await (await a.get("/api/saved")).json()).toEqual({ids:[]});
    expect(await (await b.get("/api/saved")).json()).toEqual({ids:["r2"]});
  } finally { await a.dispose(); await b.dispose(); }
});
Reasoning
The second namespace remains unchanged and both request contexts are disposed.

Interview reasoning and next step

In interviews, name the resource owner, lifecycle and cleanup boundary.

In interviews, name the resource owner, lifecycle and cleanup boundary. Explain a failure that retries might conceal. Prefer a small explicit fixture over a generic setup layer whose state transitions nobody can explain.

Next, add accessibility and layout evidence while keeping their coverage limits honest.

Optional stretch — after the core task works: Reverse test order and run with two workers.

Computer practice: download the companion lab at https://applydjinn.com/typorithm/qa-lab-20261105.zip. Read its README, install the pinned tools and run the accepted examples before attempting a starter. The mobile reader preserves your reading and self-check progress; the local lab runs browser tests separately.

Make setup and teardown explicit without deleting another test’s data.