Grasping Parallelism in Playwright
Playwright Test naturally runs tests in parallel using multiple worker processes. But guess what? You have the reins to control and configure this parallelism based on your specific needs. Here are a few things to keep in mind:
- Tests from different files run concurrently by default, while tests within a single file follow a sequential order.
- With
test.describe.configure
, you can enable parallel execution of tests within a single file. - You can make all tests in your project run in parallel using the
fullyParallel
configuration option. - You can switch off parallelism entirely by setting the worker count to one.
Mastering Worker Processes
All About Worker Processes
Each test in Playwright operates in its worker process—an independent OS process managed by the test runner. These workers start their browsers and maintain identical environments. Communication between workers is a no-go, but Playwright Test smartly reuses a single worker as much as it can to speed up testing.
Regulating Worker Processes
You can dictate the maximum number of parallel worker processes either via the command line or the configuration file. Let's see how:
Command line:
npx playwright test --workers 4
Configuration file (playwright.config.ts
):
import { defineConfig } from '@playwright/test';
export default defineConfig({
workers: process.env.CI ? 2 : undefined,
});
Disabling Parallelism
Don't want to run tests in parallel? Just set the worker count to one through the command line or the configuration file.
Command line:
npx playwright test --workers 1
Configuration file (playwright.config.ts
):
import { defineConfig } from '@playwright/test';
export default defineConfig({
workers: 1,
});
Parallelizing Tests in One File
Normally, tests in a single file run sequentially. However, you can tweak them to run concurrently using either test.describe.configure()
or the fullyParallel
configuration option.
The test.describe.configure()
Method
To execute tests in a single file simultaneously, call the test.describe.configure()
method:
import { test } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });
The fullyParallel
Configuration Option
To make all tests run in parallel across your entire test suite, turn the fullyParallel
option to true
in your configuration file:
import { defineConfig } from '@playwright/test';
export default defineConfig({
fullyParallel: true,
});
Running Tests Sequentially
Sometimes, you might need to execute tests in the same file sequentially. That's where Playwright's serial
mode configuration comes in handy—it runs tests one after another, skipping the rest if one fails. Still, it's generally better to design your tests to be isolated and independently runnable.
Here's how you can switch to serial mode:
import { test, Page } from '@playwright/test';
test.describe.configure({ mode: 'serial' });
Splitting Tests Across Multiple Machines
Want to divide your test suite across different machines? With Playwright Test, you can "shard" it. Just add the --shard=x/y
option to the command line:
npx playwright test --shard=1/3
npx playwright test --shard=2/3
npx playwright test --shard=3/3
In this example, the test suite splits into three parts, each command running one part.
Determining Test Order
Since Playwright runs tests in parallel by default, the execution order isn't guaranteed. You can sort test files alphabetically or use a "test list" file if you need a specific order.
Alphabetical Test File Sorting
For alphabetical sorting, prefix each file name with a three-digit number (e.g., 001, 002, 003).
Using a Test List File
To use a test list file, wrap your tests in helper functions within their files. Then, create a new file that imports all helpers and wraps them in test.describe
. Lastly, turn off parallelism and set Playwright to use only the test list file.
Check out this example:
feature-a.spec.ts
:
import { test, expect } from '@playwright/test';
export default () => {
test('feature-a example test', async ({ page }) => {
// Test implementation
});
}
feature-b.spec.ts
:
import { test, expect } from '@playwright/test';
export default () => {
test.use({ viewport: { width: 500, height: 500 } });
test('feature-b example test', async ({ page }) => {
// Test implementation
});
}
test.list.ts
:
import { test } from '@playwright/test';
import featureBTests from './feature-b.spec';
import featureATests from './feature-a.spec';
test.describe(featureBTests);
test.describe(featureATests);
playwright.config.ts
:
import { defineConfig } from '@playwright/test';
export default defineConfig({
workers: 1,
testMatch: 'test.list.ts',
});
Wrap-Up
Congratulations! You've mastered running Playwright tests in parallel, controlling worker processes, configuring parallelism, and even managing test order. Implementing these techniques'll optimize your test suite for maximum efficiency and boost your software quality. Happy testing!