skip to content

Division of Work

How the runner cuts a suite into work: separate worker processes, whether files or individual tests run side by side, and splitting one run across several machines.

on this pageshow

explore

questions

14

In Playwright, what does the fullyParallel config option change about test scheduling?

level: juniorimportance: must knowfreq 82%

answer

  1. What gets handed to a worker
  2. File is the default unit
  3. Order inside a file disappears
  4. Config flag plus a CLI form
  5. Describe modes override the flag

basics

~20 s

Playwright runs test files in parallel but keeps tests inside a file in order in one worker. Setting fullyParallel true schedules every individual test independently, so tests in the same file can also run at the same time.

solid answer

~40 s

By default Playwright's unit of parallelism is the file: the runner hands each spec file to a worker process, and the tests inside that file run in declaration order in that one worker. Setting `fullyParallel: true` in `playwright.config.ts`, or passing `--fully-parallel` for a single run, changes the unit to the individual test, so two tests from the same file may execute concurrently in different workers and in no guaranteed order. It is purely a scheduling switch: it changes how many tests are in flight, not what any test does. Per-group overrides still win over it, so `test.describe.configure({ mode: 'serial' })` pins a block back to one ordered worker, and `mode: 'default'` opts a whole file back out of fully parallel scheduling.

code

typescript · 8 lines
typescript
// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests/payroll',
  // Schedule every test independently, not just every spec file.
  fullyParallel: true,
});

go deeper

for a junior

Remember the one-line contrast: files run together by default, tests inside a file run in order, and fullyParallel makes the individual test the unit instead.

for a middle

Explain the mechanics: dispatch unit, loss of declaration order, and how describe-level modes override the config flag per file or per block.

for a senior

Show you can roll it out safely on a real suite, predicting which shared state and which hooks change behaviour before flipping it on in CI.

for a principal

Own the policy question of whether the suite is parallel by default with marked exceptions, and what that implies for load on the system under test.

## The default schedule Playwright's runner starts a pool of worker processes and dispatches work to them. Out of the box the thing it dispatches is a whole **test file**. That gives you two guarantees that beginners rely on without noticing: - Different spec files run **at the same time**, in different worker processes. - Tests **inside** one file run **one after another, in declaration order**, in a single worker. - A failing test does **not** stop the tests declared after it in that file; they still run. So a payroll regression suite with 40 spec files already parallelises 40 ways, but a single file holding 30 tests is a 30-test-long queue on one worker. ## What `fullyParallel: true` changes Setting `fullyParallel: true` in `playwright.config.ts` moves the unit of dispatch from the file down to the **individual test**. After that: - Any test may be picked up by any free worker, including two tests declared in the same file. - **Declaration order stops being an execution order.** Test 2 in a file may start before test 1 finishes, or before it starts at all. - The wall-clock ceiling of the run drops from *longest file* to roughly *longest single test*, subject to how many workers you allow. The same switch is available for one run from the command line as `--fully-parallel`, which is handy for asking "is this suite actually parallel-safe?" without committing the config change. ## The two schedules side by side | | default | `fullyParallel: true` | |---|---|---| | Unit dispatched | one spec file | one test | | Order inside a file | declaration order | none guaranteed | | Tests of one file share a worker | yes | not necessarily | | Wall-clock floor | slowest file | slowest test | | Effect of a failure on later tests in the file | none | none | ## Precedence: config, file, group `fullyParallel` is the baseline, not the last word. `test.describe.configure({ mode })` overrides it for the scope it is called in, and the innermost call wins: 1. Call `test.describe.configure({ mode: 'parallel' })` at the top of one file to get test-level parallelism for that file only, without turning the flag on globally. 2. Call `test.describe.configure({ mode: 'default' })` at the top of a file to opt that file out again when the rest of the repository runs fully parallel. 3. Call `test.describe.configure({ mode: 'serial' })` inside a `test.describe` block to pin just that block to one worker in order. That layering is what lets a large suite adopt `fullyParallel` in one commit and quarantine the handful of order-dependent files instead of holding the whole suite back. ## Practical consequences to expect - Anything a test leaves behind, such as a queued payroll export or a mutated pay period, can now be observed by a test that used to run safely after it. The failure surfaces as a flake, not as a compile error. - Hooks change shape: a `beforeAll` in a parallel-mode block runs once **per worker** that picks up a test from it, so it may run several times in one run. - More concurrency means more browsers and more load on the system under test at once; a payroll backend that is fine with four concurrent sessions may not be fine with twenty. - The reported per-test durations do not change, only the total wall clock, so a run that looks "three times faster" is the scheduler working, not the tests getting quicker. ## How to say it in an interview Lead with the unit of parallelism. "By default the file is the unit and tests inside it are ordered on one worker; `fullyParallel` makes the test the unit, so nothing inside a file is ordered any more." Then add the escape hatch in the same breath: describe-level modes override the flag, so you turn it on globally and mark the dependent groups rather than choosing one setting for the entire repository.

  • If fullyParallel is on, how do you keep one order-dependent spec file running the old way?
    Call `test.describe.configure({ mode: 'default' })` at the top level of that file. It restores the default schedule for that file only: its tests run in declaration order in a single worker, while every other file keeps the fully parallel behaviour from the config.
  • Does fullyParallel change how many browsers are open at once?
    Indirectly. It raises how many tests are eligible to run concurrently, so the workers you allow are more likely to all be busy, each with its own browser. The concurrency ceiling itself is still the worker count, not the flag.

Default mode is one checkout lane per shopping basket; fullyParallel opens a lane for every single item.

saying these in an interview costs you the question

  • Says tests in one file always run in parallel
  • Thinks fullyParallel makes each test faster
  • Believes declaration order is honoured under fullyParallel
  • Assumes the config flag cannot be overridden per file
  • Confuses parallel scheduling with retrying failed tests
open as a page

What does Playwright's --shard=1/4 flag do to the set of tests a run executes?

level: juniorimportance: must knowfreq 68%

basics

~20 s

Playwright splits the selected tests into four disjoint groups and runs only the first. Each shard is an independent run on its own machine, so all four have to be executed before the suite is covered.

open as a page

In Playwright, what does the workers option control and what is its default value?

level: juniorimportance: must knowfreq 72%

basics

~20 s

The workers option sets how many test processes Playwright runs at once, each running one test at a time. It defaults to half the machine's logical CPU cores, and --workers or -j overrides it for a run.

open as a page

In Playwright, when a test.describe.serial group hits a failure, what happens to the rest?

level: middleimportance: must knowfreq 66%

basics

~20 s

The tests after the failure are skipped rather than run. A retry does not resume at the failure: the whole group reruns from its first test, because serial mode treats the block as one ordered unit.

open as a page

How does Playwright's fullyParallel setting change the way --shard divides a suite?

level: middleimportance: must knowfreq 54%

basics

~20 s

It changes the unit being split. With fullyParallel off, Playwright shards whole spec files, so one file never spans two shards. With it on, individual tests are sharded, so shards get roughly equal test counts.

open as a page

What happens to a Playwright worker process when one of its tests fails?

level: middleimportance: must knowfreq 58%

basics

~20 s

Playwright throws the worker away. The process is shut down after the failure and a fresh worker process, with a new browser, picks up the remaining tests, so nothing the failing test left behind reaches them.

open as a page

In Playwright, what does test.describe.configure({ mode: 'default' }) do in a fullyParallel project?

level: middleimportance: should knowfreq 44%

basics

~20 s

Default mode opts that scope back out of test-level parallelism. The tests run in declaration order in one worker again, but stay independent: a failure skips nothing after it, and a retry reruns only the failing test.

open as a page

In Playwright, how do testInfo.workerIndex and testInfo.parallelIndex differ?

level: middleimportance: should knowfreq 42%

basics

~20 s

workerIndex names a process and never repeats: every worker a run starts, including replacements after failures, gets a new one. parallelIndex names a slot between zero and workers minus one, and a replacement worker inherits it.

open as a page

Enabling Playwright's fullyParallel made one payroll spec fail intermittently in CI — how do you contain it?

level: seniorimportance: should knowfreq 51%

basics

~20 s

Confirm the schedule is the trigger by rerunning that spec with and without test-level parallelism, then apply the narrowest override that holds: default mode for the file, or a serial describe around only the dependent tests.

open as a page

Four Playwright shards each produced their own HTML report - how do you get one report for the whole run?

level: seniorimportance: should knowfreq 47%

basics

~20 s

Have every shard run the blob reporter instead of html. Each writes one archive, CI collects them into a single directory, and npx playwright merge-reports turns that directory into one HTML report covering the whole suite.

open as a page

Playwright's default worker count makes your payroll suite time out in a CPU-limited CI container - how do you diagnose and fix it?

level: seniorimportance: should knowfreq 36%

basics

~20 s

The default is half the cores the operating system reports, which is usually the host's, not the container's CPU allowance. Read the worker count from the run header, compare it with the container limit, then pin workers explicitly.

open as a page

Should a Playwright suite's shard split live in playwright.config.ts or in the CI command line?

level: principalimportance: should knowfreq 32%

basics

~20 s

Playwright treats both identically, so it is a question of ownership. The split belongs to the pipeline launching the jobs, so the --shard flag usually wins; the config option earns its place when a wrapper script starts the run.

open as a page

Playwright discards a worker process after any test failure - what does that guarantee cost a large suite?

level: principalimportance: should knowfreq 30%

basics

~20 s

It buys certainty that no test inherits a process a failure left dirty. It costs a cold browser launch plus per-worker set-up for every failure, so a red payroll run is measurably slower than a green one.

open as a page

In Playwright, why can a few test.describe.serial groups dominate a large suite's wall-clock?

level: seniorimportance: nice to knowfreq 32%

basics

~20 s

A serial group is indivisible: its tests run in order on one worker, so its total duration is a floor no extra parallelism can cut. Retries replay the whole group, so the longest chain sets the finish time.

open as a page