Playwright Parallel Execution: Workers, fullyParallel and Sharding

Playwright runs tests in parallel for free, and with sharding you can split a suite across many machines. In this lesson you will learn how workers run tests, how to tune parallelism, how to write tests that are safe in parallel and how to shard and merge results.

Advertisement

๐Ÿ“– What You’ll Learn: how workers execute tests, configuring workers, fullyParallel and parallel mode, serial mode, parallel-safe test data, worker index and worker-scoped fixtures, sharding with blob reports, and a speed checklist.


โšก Why Parallel Execution?

A suite of 300 UI tests at 10 seconds each takes 50 minutes in sequence. With 8 parallel workers it takes about 7 minutes, and split across 4 CI machines about 2 minutes. Fast feedback is what makes automation useful, and Playwright runs tests in parallel out of the box, for free.


๐Ÿ‘ท How Playwright Runs Tests in Parallel

Playwright starts several worker processes. Each worker is an independent OS process with its own browser.

  • By default, test files run in parallel across workers.
  • Tests inside one file run in order in the same worker.
  • Every test gets a fresh, isolated browser context, so tests do not share cookies or storage.
  • If a test fails, its worker is shut down and a new one starts for the remaining tests.
Running 40 tests using 4 workers
  worker 0: login.spec.ts     (5 tests in order)
  worker 1: cart.spec.ts      (8 tests in order)
  worker 2: checkout.spec.ts  (6 tests in order)
  worker 3: search.spec.ts    ...

๐Ÿ”ข Control the Number of Workers

// playwright.config.ts
export default defineConfig({
  workers: process.env.CI ? 2 : undefined,  // undefined = about half of the CPU cores
});
npx playwright test --workers=4
npx playwright test --workers=50%     # half of the CPU cores
npx playwright test --workers=1       # no parallelism (debugging)

๐Ÿ’ก Tip: More workers is not always faster. Each worker runs a browser, which needs CPU and memory. On a 2-core CI runner, 2 workers is usually the sweet spot; measure before increasing.


๐Ÿงต Run Tests Inside a File in Parallel

To parallelize individual tests, not only files:

// Whole project
export default defineConfig({
  fullyParallel: true,
});
// One file only
import { test } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });

test('search by name', async ({ page }) => { /* ... */ });
test('search by category', async ({ page }) => { /* ... */ });

With fullyParallel: true the workload is spread more evenly, especially when files have very different sizes.

Opting out: serial tests

Some flows must run in order (for example a multi-step wizard that shares state). Mark them serial; if one fails, the rest are skipped:

test.describe.configure({ mode: 'serial' });

Use serial mode sparingly: independent tests are faster and easier to debug (tags and grouping).


๐Ÿงช Write Tests That Are Safe to Run in Parallel

Parallel runs expose hidden dependencies. Follow these rules:

ProblemExampleSolution
Shared test dataTwo tests edit the same user profileCreate unique data per test
Shared account stateTests add and remove items in the same cartOne account per worker, or reset state per test
Order dependenceTest B expects test A’s order to existEach test creates what it needs (via API)
Global limitsThird-party sandbox allows one sessionMock it or run those tests serially

Use the worker index to give each worker its own data:

test('create a project', async ({ page }, testInfo) => {
  const name = 'Project-w' + testInfo.workerIndex + '-' + Date.now();
  // ...create and assert the project with a name no other worker uses
});

For per-worker accounts, a worker-scoped fixture creates one account per worker and reuses it for all its tests (custom fixtures):

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

export const test = base.extend<{}, { account: { username: string; password: string } }>({
  account: [async ({}, use, workerInfo) => {
    const username = 'qa_user_' + workerInfo.workerIndex;  // e.g. qa_user_0, qa_user_1 ...
    // create or reset this user via API here
    await use({ username, password: process.env.QA_PASSWORD ?? '' });
  }, { scope: 'worker' }],
});

๐Ÿงฉ Sharding: Split the Suite Across Machines

Sharding divides the tests into N parts and runs each part on a different machine (or CI job):

npx playwright test --shard=1/4
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4

Each shard can still use several workers. Combine shards with the blob reporter and merge the results into one HTML report:

npx playwright test --shard=1/4 --reporter=blob
# ...after all shards...
npx playwright merge-reports --reporter html ./all-blob-reports

๐Ÿ“ Note: Enable fullyParallel: true when sharding, so tests (not whole files) are distributed and shards finish at similar times.

The complete GitHub Actions matrix setup is in Run Playwright tests in GitHub Actions, and merging is explained in reporters.


๐Ÿ“ Speed Checklist

  • Use fullyParallel: true for independent tests.
  • Log in once with a setup project and storageState (save login session) instead of logging in through the UI in every test.
  • Create test data through the API, not the UI (API testing).
  • Block heavy third-party resources in functional tests (network mocking).
  • Run the full browser matrix nightly, and Chromium only on pull requests (cross-browser).
  • Stop early on CI disasters with maxFailures: 10.

๐Ÿ“ Assignment

  1. Run your suite with --workers=1 and then --workers=4, and compare the total time.
  2. Enable fullyParallel: true and observe how tests from the same file now run in different workers (use --reporter=list).
  3. Log testInfo.workerIndex and testInfo.parallelIndex in a test and run with 3 workers.
  4. Make one file serial with test.describe.configure({ mode: 'serial' }), fail its first test on purpose, and observe the skipped tests.
  5. Run the suite as three shards in three terminals with --reporter=blob, then merge the reports.
  6. Bonus: create a worker-scoped fixture that returns a different username per worker.

โ“ FAQ

๐Ÿค” Does Playwright run tests in parallel by default?

Yes. Test files run in parallel across worker processes. Tests inside a file run in order unless you set fullyParallel: true or mode: 'parallel'.

๐Ÿค” How do I set the number of workers in Playwright?

Use workers in the config (a number or a percentage such as '50%') or --workers=N on the command line.

๐Ÿค” How do I run Playwright tests sequentially?

Use --workers=1 for the whole run, or test.describe.configure({ mode: 'serial' }) for tests that must run in order.

๐Ÿค” What is sharding in Playwright?

Splitting the suite into parts with --shard=x/y so each part runs on a different machine, then merging the blob reports into one report.

๐Ÿค” Why do my tests fail only when run in parallel?

They share data or state, such as the same user, cart or record. Give each test or worker its own data, and avoid dependencies between tests.


๐ŸŽฏ Next Steps

๐ŸŽฌ Watch the full Playwright tutorial series on YouTube!

Subscribe to SqaToolsByDY • Practice on sqatools.in

1 thought on “Playwright Parallel Execution: Workers, fullyParallel and Sharding”

Leave a Comment