A JUnit 5 method annotated @RepeatedTest(100) keeps executing all remaining repetitions even after the first several have failed, wasting minutes of build time. How can you make it stop early, and what are the constraints on that setting?
answer
- failureThreshold attribute, JUnit 5.10+
- Default Integer.MAX_VALUE = run everything
- Must be >0 and < total repetitions
- Remaining repetitions reported as skipped, not passed
- Best-effort only under parallel execution
basics
~20 sSet the annotation's failureThreshold attribute, e.g. @RepeatedTest(value = 100, failureThreshold = 3). Once that many repetitions have failed, the remaining ones are automatically skipped rather than executed. It must be positive and smaller than the repetition count; it was added in JUnit 5.10.
solid answer
~50 sUse the `failureThreshold` attribute added in **JUnit 5.10**: ```java @RepeatedTest(value = 100, failureThreshold = 3) void stableUnderRepetition() { ... } ``` After three repetitions have failed, JUnit stops executing the rest — the remaining repetitions are reported as **skipped**, not as passed and not as failed, so the report still shows that the test did not complete cleanly. Constraints: the value must be **greater than zero and less than the total repetition count**; anything else is a configuration error. The default is `Integer.MAX_VALUE`, i.e. "never stop early", which is why the out-of-the-box behaviour runs all repetitions. The current value is visible to the test through `RepetitionInfo.getFailureThreshold()`. One caveat: if repetitions of the template are executed in parallel, the threshold is enforced on a best-effort basis — repetitions already in flight when the threshold trips will still finish.
code
java · 14 linesimport org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import static org.junit.jupiter.api.Assertions.assertTrue;
class CacheStabilityTest {
@RepeatedTest(value = 100, failureThreshold = 3)
void lookupAlwaysHitsTheCache(RepetitionInfo info) {
assertTrue(
cache.lookup("k").isPresent(),
() -> "miss on repetition " + info.getCurrentRepetition()
+ " (threshold " + info.getFailureThreshold() + ")");
}
}go deeper
It is enough to know the attribute exists and that without it every repetition runs regardless of failures.
State the attribute, its default of Integer.MAX_VALUE, the >0-and-less-than-total constraint, and that leftovers are reported as skipped.
Add the judgement call — thresholds suit stability checks but destroy the signal in rate-measuring tests — plus the best-effort behaviour under parallel invocation.
Frame it as a build-time-versus-diagnostic-signal tradeoff across the suite, and note that pre-5.10 the same behaviour required a custom test-template/condition extension.
## The default behaviour, and why it hurts Every repetition produced by `@RepeatedTest` is an independent test invocation. That independence is deliberate: if you run something 100 times to observe a failure *rate*, you want all 100 results, not just the prefix up to the first failure. So by default a failure never short-circuits the rest. The cost shows up when the code under test is genuinely broken. A repeated test with a 500 ms body and 100 repetitions burns nearly a minute in every build re-running an assertion that has already failed 40 times, and floods the report with 100 near-identical failure entries. ## `failureThreshold` JUnit 5.10 added the `failureThreshold` attribute to `@RepeatedTest`: ```java @RepeatedTest(value = 100, failureThreshold = 5) void idempotentUnderRepetition() { ... } ``` Semantics: the engine counts failed repetitions as it goes; as soon as the count reaches `failureThreshold`, all **remaining** repetitions are skipped rather than executed. Key details: - The default is `Integer.MAX_VALUE`, which is the encoding of "no threshold". That is why untouched repeated tests run to completion. - The threshold must be **greater than 0 and less than the total number of repetitions**. `failureThreshold = 0` and `failureThreshold >= value` are both configuration errors reported by the engine, not silently normalised. - Skipped repetitions are reported with the *skipped/aborted* outcome and a reason mentioning that the failure threshold was exceeded. They are **not** counted as passes — an important point, because a naive reading of "95 skipped" as "95 fine" would be wrong, and equally the test as a whole is still red thanks to the failures that did occur. - The configured value is readable from the test via `RepetitionInfo.getFailureThreshold()`, which was added in the same release. ## Where the threshold sits in the reporting model A `@RepeatedTest` method is a container node; the repetitions are its children. Failure counting happens across those children. The container's own result is derived from the children as usual, so a run with 5 failures and 95 skips is a failing test — the threshold saves time, it does not soften the verdict. This also means the threshold is **per method execution**, not per class or per suite. Two different repeated test methods each have their own independent counter. ## Parallel execution caveat JUnit Jupiter can execute the invocations of a test template concurrently when parallel execution is enabled and the test is not marked as requiring same-thread execution. In that mode the threshold is inherently approximate: several repetitions may be in flight when the count crosses the threshold, and those in-flight repetitions run to completion. You may therefore observe slightly more than `failureThreshold` failures. The attribute is documented as a best-effort early exit, not a hard cap, under concurrency. ## When to set it, and when not to Set it when the repetition exists as a **stability check** — "this must pass 200 times running" — because there the first handful of failures already answers the question and everything after is waste. Leave it unset when the repetition exists to **measure a rate** — for instance a randomised input test where you genuinely want to know whether it fails 2 times in 100 or 60 times in 100. Truncating the run destroys the very signal you were collecting. A reasonable default in practice is a small threshold (1-3) for high repetition counts in CI, because in CI you are asserting stability, and a low threshold turns a minute-long failure into a couple of seconds while still proving the point. ## Version note and fallback Before JUnit 5.10 there was no built-in way to stop a repeated test early. Teams on older versions either lived with the full run, kept repetition counts low, or wrote a custom `TestTemplateInvocationContextProvider` / execution-condition extension that tracked failures in the extension store and aborted subsequent invocations. If a candidate proposes that extension-based approach, they are describing what the framework now does for you — worth knowing as history, not as the current answer.
- After the failure threshold trips, how are the untouched repetitions reported — as passed, failed, or something else?They are reported as skipped/aborted, with a reason indicating the failure threshold was exceeded. They are deliberately not counted as passes, because they never ran, and not as failures, because nothing failed in them. The overall test still fails on the strength of the repetitions that genuinely failed, so the build result is unchanged — only the runtime is shorter.
- Would you set failureThreshold on a repeated test whose purpose is to sample randomised inputs?Usually no. There the point of running many repetitions is to measure how often the property is violated, and cutting the run short after a few failures throws that measurement away. failureThreshold fits stability checks — 'must pass N times in a row' — where the first couple of failures already answer the question and everything afterwards is wasted build time.
saying these in an interview costs you the question
- Thinking @RepeatedTest stops at the first failure by default.
- Setting failureThreshold equal to or greater than the repetition count (a configuration error).
- Claiming the skipped repetitions count as passing.
- Treating the threshold as a hard guarantee even when repetitions run in parallel.
- Assuming failureThreshold exists in every JUnit 5 version rather than 5.10 and later.