A parallel run can dispatch by case, by class or by process — what does each unit make contended?
answer
- Granularity the runner hands one worker
- Everything above that scope becomes shared
- Case, class, file or process
- Processes split memory, not the deployed target
basics
~20 sThe unit of parallelism decides what two test workers can touch at the same moment. Dispatching whole classes protects per-class setup; dispatching individual cases contends it; separate processes end in-memory sharing but leave every external resource shared.
solid answer
~50 sThe unit is the granularity the runner hands to one worker, and it draws the line above which everything is shared. - **Case-level**: two cases from the same class can run together, so per-class setup and any per-class holder are now used concurrently. - **Class- or file-level**: the cases of one class stay on one worker in order, so per-class setup is private again; only run-scoped values are contended. - **Process-level**: each worker is its own operating-system process, so nothing in memory is shared — paid for in start-up time and memory per worker. No unit isolates what lives outside the run: the deployed target, its data, anything bound on the host and the output directory stay shared under every model. Pick the unit whose boundary sits just above the smallest thing a case must own alone.
code
pseudocode · 8 lines# The runner's dispatch loop, one unit at a time
for unit in partition(suite, by = UNIT): # UNIT = CASE | CLASS | PROCESS
worker = pool.acquire()
worker.run(unit)
# CASE unit = one case -> per-class setup shared across workers
# CLASS unit = one whole class -> per-class setup private, run scope shared
# PROCESS unit = one class, own OS process -> memory private, target still sharedgo deeper
Be ready to say what parallel execution means at all: several cases run at the same moment on different workers instead of one after another, so anything two of them touch together has to tolerate that.
An interviewer expects you to name the units — case, class, file, process — and say for each one what stops being private. State the rule plainly: everything scoped above the unit is shared by definition.
Show that you pick the unit from an inventory of what the cases genuinely share, and that you know process isolation stops at the process boundary — the deployed target, its data and the output directory remain common. Account for the start-up and memory cost.
Own the model for the whole suite: which unit is the default, where a narrower one is allowed, and what a team must demonstrate before widening an exception. Defend the concurrency you gain against the determinism you spend.
## What the unit of parallelism is A runner never parallelises "a suite". It parallelises a **unit** — the smallest indivisible package of work it is willing to hand to one test worker. Everything scoped *inside* that unit stays private to whichever worker holds it, because only that worker is executing it. Everything scoped *outside* the unit is, by definition, reachable by every worker at the same moment. That single sentence is the entire model. Picking the unit is not a performance knob; it is a declaration about which boundaries in your code are still allowed to assume they run alone. ## The common units, and what each one contends | Unit | What one worker holds | What becomes contended | Why you would pick it | | --- | --- | --- | --- | | **Case** | one case, start to finish | per-class setup, per-class holders, anything the class scope assumed it owned | a few long cases dominate the run and must overlap | | **Class** | every case of one class, in order | run-scoped holders, and everything outside the process | per-class setup is expensive or genuinely exclusive | | **File / module** | every case in one file | the same as class, at file granularity | the code groups cases by file rather than by class | | **Process** | one class or file inside its own operating-system process | nothing in memory; everything outside the process | in-memory globals cannot be made concurrency-safe | **Case-level** is the finest unit most runners offer, and the one that surprises people. The moment two cases from the same class can run together, the per-class setup that opens something once — a connection, a temporary directory, a seeded record — is being used by two cases at the same time. Nothing in the code changed; the scope it was written against did. **Class-level** keeps the cases of one class on one worker, in order, so per-class setup is private again. What is now contended is anything scoped above the class: values held for the whole run, counters, caches, registries. **Process-level** is the only unit that removes in-memory sharing outright, because two operating-system processes do not share a heap. It is the blunt fix for globals you cannot change — third-party code, a library that keeps one instance per process. You pay for it in start-up time and memory: every worker loads the harness, warms its own caches and opens its own connections. ## What no unit isolates Refining the unit adds concurrency. It never adds isolation to anything that lives outside the process: 1. **The deployed target** the cases exercise, and the data behind it. 2. **Anything bound on the host** — a fixed local port, a named socket, a lock file. 3. **The output directory** the run writes evidence into. 4. **Global settings** on the target: a toggle, a system-wide configuration value, a scheduler that runs once. 5. **Anything external** the product calls out to, including quotas measured per account. This is the mistake that survives longest: a team moves to process-level workers, sees the in-memory collisions vanish, and concludes the suite is isolated. It is isolated from itself in memory only. Everything downstream is exactly as shared as it was. ## Choosing the unit - **Inventory what the cases actually share** before choosing. The unit follows the inventory, not the other way round. - **Pick the unit whose boundary sits just above the smallest thing a case must own alone.** If a case must own its setup, the class is the unit. If nothing in memory can be shared safely, the process is. - **Make exceptions narrow.** Nesting is legitimate: classes run concurrently while the cases inside one class run in order. That is a scoped statement — "this class holds something exclusive" — and it is far better than widening the exception to the whole suite. - **Measure the cost you are buying.** Finer units mean more workers, more start-ups and more simultaneous load on the one target; there is a width past which contention, not the code, decides the result. ## Why the finer unit is often worth the trouble A finer unit exposes coupling earlier, and exposure is the point. Coupling that is never exercised does not go away; it waits until someone splits a class, adds a case or runs the suite twice at once. Running at the finest unit the code honestly supports turns a latent design problem into a reproducible failure with a name on it — which is the cheapest form that problem will ever take.
- What do you pay for process-based test workers that you do not pay for thread-based ones?Each process pays its own start-up: loading the harness, warming caches, opening its own connections, plus its own memory footprint. With many short cases that fixed cost can exceed the time saved. Process workers also cannot reuse anything the suite built once in memory, so every expensive preparation is repeated per worker.
- A team runs classes concurrently but keeps the cases inside a class in order. When is that a design rather than a workaround?It is a design when someone can name the exclusive thing the class owns — a bound port, one output file, a single seeded record — and serialising inside the class is the cheapest correct way to hold it. It is a workaround when nobody can name it: the coupling is then unexamined, and it reappears the moment the class is split.
Choosing the unit is like deciding how much of a shared workshop one person books at a time: book the whole floor and nobody collides but nobody else works; book a single bench and only the tools on the wall are fought over.
saying these in an interview costs you the question
- Treats the unit of parallelism as a setting rather than a design choice
- Assumes separate processes isolate the shared deployed target
- Thinks class-level dispatch protects run-scoped holders
- Cannot say what becomes contended at each unit
- Picks the finest unit available and hopes for the best
- Ignores the start-up and memory bill of each process worker