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.
๐ 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:
| Problem | Example | Solution |
|---|---|---|
| Shared test data | Two tests edit the same user profile | Create unique data per test |
| Shared account state | Tests add and remove items in the same cart | One account per worker, or reset state per test |
| Order dependence | Test B expects test A’s order to exist | Each test creates what it needs (via API) |
| Global limits | Third-party sandbox allows one session | Mock 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: truefor 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
- Run your suite with
--workers=1and then--workers=4, and compare the total time. - Enable
fullyParallel: trueand observe how tests from the same file now run in different workers (use--reporter=list). - Log
testInfo.workerIndexandtestInfo.parallelIndexin a test and run with 3 workers. - Make one file serial with
test.describe.configure({ mode: 'serial' }), fail its first test on purpose, and observe the skipped tests. - Run the suite as three shards in three terminals with
--reporter=blob, then merge the reports. - 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
- ๐ญ Run Playwright Tests in GitHub Actions
- ๐ญ Run Playwright Tests in Jenkins
- ๐ญ Playwright with Docker and Kubernetes
- ๐ญ Playwright Tutorial with TypeScript
๐ฌ 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”