DIAGNOSTICS AND DELIVERY · LESSON 22 OF 24
Parallelism, retries and flaky tests
Scale execution without sharing mutable test data.
- workers
- fullyParallel
- retries
- repeat-each
Your runnable checkpoint
Extract into a new practice folder. Each download contains tests, configuration, the complete local practice app and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.
Install dependencies and browser binaries using the commands below. The test runner starts the practice server on port 4174. Tests target localhost, never the production EduPoly site.
1. Understand the concepts
Workers are separate processes used to run tests. fullyParallel allows tests within files to run in parallel. Every scenario must own its browser state and any server-side data it changes.
A retry repeats a failed test; it does not repair the cause. A test that fails first and passes on retry is flaky evidence. Investigate unstable selectors, missing waits, shared records and environmental dependencies.
The course uses zero retries locally and one in CI to preserve evidence while allowing transient failures to be diagnosed. Do not use test.only in committed suites; forbidOnly catches it in CI.
Use --repeat-each=3 with multiple workers to expose hidden order dependencies. The lab’s browser-local task storage is context-isolated, while API examples require unique namespaces. Do not solve collisions by globally resetting everyone’s data.
2. Build and run
These are complete test files and configuration. The download also includes app/ and server.mjs; preserve their paths. Stop any manually started practice server before running the suite.
npm install
npx playwright install chromium
npm run check
npm testtests/lesson.spec.ts
import { test, expect } from '@playwright/test';
for (const name of ['Worker A', 'Worker B', 'Worker C']) {
test(`isolated task for ${name}`, async ({ page }) => {
await page.goto('/');
await expect(page.getByTestId('task-row')).toHaveCount(0);
await page.getByLabel('Task title', { exact: true }).fill(name);
await page.getByRole('button', { name: 'Add task', exact: true }).click();
await expect(page.getByTestId('task-row')).toHaveCount(1);
await expect(page.getByTestId('task-row')).toContainText(name);
});
}
playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0,
workers: process.env.CI ? 2 : undefined,
timeout: 30000,
expect: { timeout: 5000 },
reporter: [['list'], ['html', { open: 'never' }]],
use: { baseURL: 'http://127.0.0.1:4174', trace: 'retain-on-failure', screenshot: 'only-on-failure' },
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
webServer: { command: 'node server.mjs', url: 'http://127.0.0.1:4174', reuseExistingServer: false, timeout: 15000 }
});
package.json
{
"name": "edupoly-playwright-parallelism-retries-and-flakes",
"version": "1.0.0",
"private": true,
"scripts": {
"test": "playwright test",
"test:headed": "playwright test --headed",
"test:ui": "playwright test --ui",
"check": "tsc --noEmit",
"report": "playwright show-report",
"serve": "node server.mjs"
},
"devDependencies": {
"@playwright/test": "1.56.1",
"typescript": "5.9.3",
"@types/node": "22.20.5"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "Node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": [
"node"
],
"noEmit": true
},
"include": [
"tests/**/*.ts",
"pages/**/*.ts",
"playwright.config.ts"
]
}
.gitignore
node_modules/
playwright-report/
test-results/
playwright/.auth/
.env
3. Trace the code and check the result
- Each generated test has a unique name.
- The empty-state assertion detects accidental shared storage.
- The final count checks that another worker did not inject extra data.
Verify your result
- Three tests pass with multiple workers.
- A repeated run does not accumulate tasks across tests.
4. Practice independently
Run this checkpoint three times per test with two workers.
Show the explanation
Use npm test -- --workers=2 --repeat-each=3. Expect nine passing executions and investigate any flaky retry instead of ignoring it.
When something goes wrong
If a browser executable is missing, run npx playwright install chromium in the project folder. If port 4174 is occupied, stop your manual practice server. Read assertion call logs for mismatched locators or expectations. Use npm run report to inspect failures; do not add fixed sleeps or broad resets to hide them.
