Skip to main content

Parallel CI Causes Test Data Collisions

Short answer

Enabling --workers=4 without isolation turns one green spec into four races on SAVE20, test@company.com, and cart id 123. Fix before adding shards: runId on every seed route, unique emails per worker, and probe Assert—not serial suites or global DB wipes.

Part of Common E2E testing gotchas.

Symptom

  • Suite was green serial; red after parallel CI or fullyParallel: true
  • Intermittent "email already registered", "coupon already redeemed", "insufficient inventory"
  • Worker 3 passes, worker 1 fails on the same spec—swap on retry
  • Local laptop green, GitHub Actions red at --workers=4

Root cause

Parallel workers share mutable identifiers in staging:

  • Global promo codes and gift cards
  • Fixed test emails and phone numbers
  • Hard-coded SKUs with quantity 1
  • Shared storageState and admin sessions (auth pollution)

Playwright parallelism is correct—the Arrange layer assumed a single runner. See world-state mutation.

Fix: namespaced Arrange per worker

const runId = `w${test.info().workerIndex}-${test.info().parallelIndex}-${Date.now()}`;

test.beforeEach(async ({ request }) => {
await request.post('/api/test/reset-world', { data: { runId } });
});

test('applies fresh coupon', async ({ page, request }) => {
const { code } = await request.post('/api/test/seed-coupon', {
data: { runId, discountPercent: 20 },
}).then(r => r.json());

await page.goto(`/checkout?run=${runId}`);
await page.getByLabel('Promo code').fill(code);
await page.getByRole('button', { name: 'Apply' }).click();

await expect.poll(async () => {
const probe = await request.get(`/api/test/probe-cart?runId=${runId}`).then(r => r.json());
return probe.discountCents;
}).toBeGreaterThan(0);
});

Each worker operates in its own runId universe—collisions disappear without test.describe.serial.

Build the harness: seed routes and probe Assert. Wire CI: GitHub Actions parallel.

Reproduce before you shard

StepCommand / action
Local parallelnpx playwright test --workers=4
Single filenpx playwright test checkout.spec.ts --workers=4
Isolate workerDEBUG=pw:api npx playwright test --workers=1 to confirm Arrange bug

Do not add GitHub Actions shards until --workers=4 is green locally—sharding multiplies collision surface without fixing roots.

Anti-patterns

Anti-patternWhy it failsBetter approach
test.describe.serial for whole suiteHides debt; slow CIrunId isolation
Nightly DB reset onlyPRs still racePer-run seed routes
Random suffix in spec onlyDB row not keyedServer accepts runId on insert
Raise timeout to 120sMasks contentionUnique entities per worker
One shared staging couponFirst worker winsMint coupon in seed route

TestChimp workflow

/testchimp init adds reset-world, seed-*, and probe-* routes with runId conventions your agents reuse. /testchimp test on PRs extends Arrange when new specs need worker-safe data—markdown plans document which entities must be namespaced.

Frequently asked questions

How many workers should we use in CI?

Start with 4 locally until green, then match CPU on GitHub runners. More workers without runId isolation only increases collision rate.

Is test.describe.serial ever OK?

Rarely—for true singleton resources you cannot namespace (legacy mainframe). Default to isolation; serial is a last resort with a ticket explaining why.

Do we need a database per worker?

Usually no—runId columns or schemas in shared staging are enough. Containers per job work but cost more; pick based on infra budget.

Sharding vs workers—what is the difference?

Workers run tests concurrently in one job; shards split the suite across jobs. Both require isolation—sharding does not fix shared coupons.

Flakes started after merging a new spec—why?

New spec may use a hard-coded email or coupon the rest of the suite already consumes. Audit Arrange for fixed identifiers.

Can storageState cause parallel collisions?

Yes—one admin session across workers causes role and tenant bleed. See shared auth state gotcha; seed per worker or use API session headers.

Does TestChimp help with parallel CI setup?

/testchimp init scaffolds runId seed and probe routes—the same harness agents extend when /testchimp test adds specs on PRs. You keep Playwright sharding; data layer becomes safe.

Should we disable fullyParallel?

Disabling parallelism slows CI without fixing the bug. Fix Arrange; keep fullyParallel for independent specs.

Apply these patterns in your repo

Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.

Start free on TestChimp · Book a demo