How does Surefire use forkCount, reuseForks, and parallel to run tests, and how do these interact?
answer
- fork = process, parallel = threads
- forkCount 0 / N / 2C
- reuseForks recycle vs fresh-per-class
- JUnit5 = junit-platform.properties
- shared state & ports = flakiness
basics
~20 sforkCount 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 sSurefire 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<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
Know tests run in a separate forked JVM by default.
Explain forkCount as number of JVMs and reuseForks as JVM recycling.
Tune forkCount/parallel for speed while controlling isolation and per-fork resources; diagnose flakiness from shared state.
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.