Q36 of 42 · Playwright

What's your strategy for handling extremely long E2E user journeys (10+ steps)?

PlaywrightSeniorplaywrighttest-designe2e-strategysenior

Short answer

Short answer: Don't write one mega-test. Decompose into independent stages — each verifiable from a known starting state via API setup. Reserve one canary test for the full end-to-end happy path; let per-stage tests cover the depth. Fixtures + `storageState` + API seeding make 'jump to step 7' cheap.

Detail

A 10+ step journey written as a single test is brittle: failure at step 8 means you've already invested 7 steps' worth of time, the failure message is far from the cause, and re-running for debugging is expensive.

Decomposition pattern:

  1. Identify natural breakpoints. A 12-step "place an order" journey decomposes into: register, add to cart (×3 items), apply coupon, checkout, fill shipping, fill payment, confirm, view confirmation. Eight breakpoints.

  2. Set up state via API at each starting point. POST /api/users, POST /api/cart, POST /api/coupons/apply — whatever lets you land at a specific step without going through the UI.

  3. One canary for the full happy path. Runs less frequently (PR-to-main or nightly) and asserts the journey is composable end-to-end.

  4. Per-stage tests for the depth — each starts from the API-prepared state for its stage, exercises that step's UI, and asserts the next state.

// canary.spec.ts — runs nightly
test('full checkout happy path', async ({ page }) => {
  // 12 steps; only one test, only the golden path
});

// stage-shipping.spec.ts — runs every PR
test.beforeEach(async ({ page, request }) => {
  await request.post('/api/test-setup/cart-with-items', { data: { items: [...] } });
  await page.goto('/checkout/shipping');
});

test('shipping form validates postcode', async ({ page }) => { /* ... */ });
test('shipping form rejects PO boxes', async ({ page }) => { /* ... */ });
test('shipping form accepts Saturday delivery option', async ({ page }) => { /* ... */ });

Why this scales:

  • Faster feedback per stage.
  • Failures localised to the actual stage.
  • Test files mirror the user journey; new tester finds them by name.
  • CI parallelism cleaner — stages are independent.

Watch-outs:

  • Test setup endpoints (/api/test-setup/...) only exist in non-prod environments. Gate them behind an env flag and a backdoor token.
  • Stage independence. If stage 7 mutates global state stage 5 expects, isolation breaks. Treat stages as composable but state-isolated; reset between tests.
  • Don't decompose to dust. If a single stage is genuinely two clicks, don't split into two specs. The right granularity is "one logical decision the user makes".

The senior signal: distinguishing the canary's role (sanity end-to-end) from per-stage depth, and the API-setup discipline that makes decomposition cheap.

// WHAT INTERVIEWERS LOOK FOR

Decomposition by API setup + per-stage tests + one canary, naming the stage-isolation discipline, and the test-setup endpoint pattern with env gating.

// COMMON PITFALL

Writing one 12-step mega-test and being surprised when it's flaky and slow — every change ripples through the whole journey.