skip to content

How does Surefire use forkCount, reuseForks, and parallel to run tests, and how do these interact?

level: seniorimportance: should knowfreq 52%

answer

  1. fork = process, parallel = threads
  2. forkCount 0 / N / 2C
  3. reuseForks recycle vs fresh-per-class
  4. JUnit5 = junit-platform.properties
  5. shared state & ports = flakiness

basics

~20 s

forkCount sets how many separate JVMs run tests at once (e.g. 1C = one per CPU core); reuseForks decides whether a JVM is reused across test classes or recycled. parallel controls threads WITHIN a JVM (methods/classes) via the provider. Forking = process-level, parallel = thread-level.

solid answer

~40 s

Surefire runs tests in forked JVMs by default. forkCount controls how many JVMs run concurrently - a number, 0 (run in Maven's JVM, no fork), or a multiplier like 2C meaning 2 per available core. reuseForks (default true) keeps a forked JVM alive across multiple test classes; set it false to get a fresh JVM per class for stronger isolation at a startup cost. Separately, parallel (suites/classes/methods/all, JUnit4/TestNG) plus threadCount/useUnlimitedThreads enables multithreading WITHIN each fork. So total concurrency is roughly forkCount JVMs x threads-per-fork. The two combine: e.g. forkCount=4 reuseForks=true with no parallel gives 4 JVMs each running classes sequentially. Watch for thread-safety: static/shared state, ports, temp files, and test ordering assumptions break under parallelism. JUnit 5 uses its own junit.jupiter.execution.parallel.* config rather than Surefire's parallel.

code

xml · 13 lines
xml
<plugin>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>3.2.5</version>
  <configuration>
    <forkCount>1C</forkCount>
    <reuseForks>true</reuseForks>
    <parallel>classes</parallel>
    <threadCount>4</threadCount>
    <systemPropertyVariables>
      <tmpDir>target/fork-${surefire.forkNumber}</tmpDir>
    </systemPropertyVariables>
  </configuration>
</plugin>

go deeper

for a junior

Know tests run in a separate forked JVM by default.

for a middle

Explain forkCount as number of JVMs and reuseForks as JVM recycling.

for a senior

Tune forkCount/parallel for speed while controlling isolation and per-fork resources; diagnose flakiness from shared state.

for a principal

Set a suite-wide concurrency strategy (CPU multipliers, per-fork resource isolation, JUnit5 vs JUnit4 model) and enforce test-thread-safety standards.

## Why forking By default Surefire launches a **separate JVM** (a *fork*) to run tests, isolating them from Maven's own process and from each other's classloader/static state. ## forkCount - process-level parallelism - `forkCount=1` (default): one forked JVM. - `forkCount=0`: no fork - tests run inside Maven's JVM (fastest startup, weakest isolation; some agents/classloaders misbehave). - `forkCount=N`: up to N JVMs run concurrently. - `forkCount=2C`: a multiplier - `2 x number of CPU cores`. - `forkCount=1.5C` etc. are allowed. ## reuseForks - recycling JVMs - `reuseForks=true` (default): a forked JVM is **reused** to run many test classes in sequence - amortizes JVM startup. - `reuseForks=false`: a **fresh JVM per test class** - maximum isolation (e.g. when classes mutate global state or System properties), at the cost of repeated startup. ## parallel - thread-level parallelism within a fork For JUnit 4 / TestNG, Surefire's `parallel` runs tests on multiple threads *inside* each fork: - values: `methods`, `classes`, `both`/`all`, `suites`, `suitesAndClasses`, etc. - `threadCount` caps threads; `useUnlimitedThreads=true` removes the cap; `perCoreThreadCount` scales by cores. ```xml <configuration> <forkCount>1C</forkCount> <reuseForks>true</reuseForks> <parallel>methods</parallel> <threadCount>4</threadCount> </configuration> ``` For **JUnit 5 (Jupiter)**, parallelism is configured by the engine, not Surefire's `parallel` property: ```properties # src/test/resources/junit-platform.properties junit.jupiter.execution.parallel.enabled=true junit.jupiter.execution.parallel.mode.default=concurrent ``` ## How they combine Total concurrent test execution is approximately `forkCount` JVMs, each possibly running `threadCount` threads. `forkCount=4` + `parallel=classes`/`threadCount=2` could run ~8 tests at once. ## Pitfalls - **Shared mutable state**: statics, singletons, in-memory caches collide across threads. - **External resources**: hard-coded ports, files, or a single DB schema cause flakiness - use random ports / per-fork temp dirs (the `${surefire.forkNumber}` placeholder helps). - **Order dependence**: parallel runs expose tests that secretly depend on execution order. - **Heap/CPU**: too many forks/threads can thrash and slow the suite.

  • What does forkCount=0 do and what's the risk?
    Tests run inside Maven's own JVM with no isolation; faster startup but shared classloader/static state and some Java agents behave incorrectly.
  • You set forkCount but tests still flake. What's a likely cause?
    Shared resources across forks - same port, file path, or DB schema. Use ${surefire.forkNumber} to give each fork its own ports/dirs.
  • How do you parallelize JUnit 5 tests?
    Not via Surefire's parallel; enable junit.jupiter.execution.parallel.enabled=true in junit-platform.properties.

forkCount is how many kitchens you open; parallel is how many cooks work in each kitchen. reuseForks is whether a kitchen keeps cooking dish after dish or gets torn down and rebuilt for every dish.

saying these in an interview costs you the question

  • Saying forkCount and parallel are the same thing.
  • Claiming reuseForks=false speeds things up (it adds startup cost).
  • Using Surefire's parallel property to control JUnit 5 concurrency.

context