Skip to content
SDETRole
SDETRole

Playwright interview question

How does Playwright run tests in parallel, and how would you scale a suite across CI machines?

advanced 5+ yrs #parallelism#sharding#workers#ci#scalability

Short answer

Playwright runs test files in parallel worker processes; fullyParallel also splits tests within a file. Each worker has its own browser and each test a fresh context. Scale out with --shard=x/n in a CI matrix, the blob reporter and merge-reports. Tests must be independent with isolated data.

Answer

Playwright Test parallelises with worker processes. By default, test files are distributed across workers and tests inside a file run in order in the same worker. Setting fullyParallel: true (or test.describe.configure({ mode: 'parallel' }) for one file) lets individual tests from the same file run in different workers. Each worker launches its own browser, and every test gets a fresh browser context, so tests are isolated at the browser level. To go beyond one machine, shard the suite across CI jobs and merge the reports. The hard part is not configuration; it is making tests independent, especially their data.

Configuration

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  fullyParallel: true,
  workers: process.env.CI ? 4 : undefined,   // undefined = half the logical CPU cores
  retries: process.env.CI ? 2 : 0,
  forbidOnly: !!process.env.CI,
  reporter: process.env.CI ? 'blob' : 'html',
});
  • workers can be a number or a percentage string such as '50%'. More workers than the machine can handle makes tests slower and flakier, not faster; measure.
  • workers: 1 disables parallelism (for debugging).
  • test.describe.configure({ mode: 'serial' }) makes a group run in order and skips the rest after a failure. Use sparingly: it signals tests that depend on each other.

Worker identity and data isolation

Parallel tests collide when they share server-side state: the same user’s cart, the same unique email, a global settings toggle. Strategies:

  • Create data per test through the API in a fixture, with unique values.
  • Allocate per-worker accounts using testInfo.parallelIndex (stable 0..workers-1, reused when a worker restarts) or workerIndex (unique per worker process).
  • Tests that change global settings go into a separate project that runs with workers: 1, or behind a dependency.
test('profile update', async ({ page }, testInfo) => {
  const email = `qa+${testInfo.parallelIndex}-${Date.now()}@example.com`;
  // ...
});

Note that when a test fails, its worker is torn down and a new one started for the remaining tests, which re-runs worker-scoped fixtures.

Sharding across machines

npx playwright test --shard=1/4
npx playwright test --shard=2/4
# ...

GitHub Actions example:

jobs:
  e2e:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shardIndex: [1, 2, 3, 4]
        shardTotal: [4]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: blob-report-${{ matrix.shardIndex }}
          path: blob-report
          retention-days: 1

  merge-reports:
    if: ${{ !cancelled() }}
    needs: [e2e]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - uses: actions/download-artifact@v4
        with: { path: all-blob-reports, pattern: blob-report-*, merge-multiple: true }
      - run: npx playwright merge-reports --reporter html ./all-blob-reports
      - uses: actions/upload-artifact@v4
        with: { name: html-report, path: playwright-report }

Sharding splits by test when fullyParallel is on, and by file otherwise, so with large uneven files, shards can be unbalanced. Splitting big files or enabling fullyParallel evens them out.

Making it actually faster

  • Find the slowest tests in the report; often a few UI setups dominate. Move setup to API calls and reuse auth via storageState.
  • Run only affected tests on PRs (--only-changed in recent versions compares against a git ref) and the full suite on main or nightly.
  • Balance cost: four shards with four workers each is sixteen browsers hitting your test environment. Make sure the environment can take the load, or you will create flakiness.

What interviewers look for

  • Accurate model: workers, file-level versus test-level parallelism, per-test contexts.
  • Sharding with a CI matrix and merging blob reports.
  • Data isolation strategies, parallelIndex/workerIndex, and limited use of serial mode.
  • Pragmatism about worker counts and environment capacity.

A strong answer covers

  • Worker processes; files in parallel by default, fullyParallel for tests within a file
  • Each worker has its own browser; tests get isolated contexts
  • Sharding across machines with --shard and a CI matrix
  • Merges reports with the blob reporter and merge-reports
  • Data isolation and shared-state problems; serial mode as an exception
Practise answering under time pressure

Type to search across SDETRole.

Full-text search by Pagefind · runs in your browser Open search page →