@RepeatedTest is commonly used to run the same test logic multiple times, but it becomes far more practical when combined with repetition metadata that can track failures across repetitions. In newer JUnit versions, repetition-aware APIs allow a test to react with failures counts.
This is particularly useful for validating retry-like behavior, flaky integrations, or stability-sensitive logic where occasional failure is tolerated but persistent failure should abort further repetitions.
Use Cases
- Intelligent retries
Adjust test behavior based on how many previous attempts have failed (for example, retry normally at first, then change strategy).
- Progressive diagnostics
Enable additional logging, tracing, or state dumps only after one or more failures to reduce noise and overhead.
- Fail-fast thresholds
Abort or short-circuit further meaningful work once a defined number of failures has been reached.
- Backoff or stabilization logic
Increase delays, timeouts, or waits after repeated failures when testing unstable or eventually consistent systems.
- Adaptive assertions
Start with relaxed checks and enforce stricter assertions only if failures continue.
- Flaky test characterization
Differentiate transient failures from persistent ones by observing how quickly failures accumulate across repetitions.
Example
package com.logicbig.example;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import org.opentest4j.TestAbortedException;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ProgressiveRetryTest {
@RepeatedTest(5)
void flakyOperation(RepetitionInfo repetitionInfo) {
System.out.println("Current repetition: " +
repetitionInfo.getCurrentRepetition());
int failuresSoFar = repetitionInfo.getFailureCount();
if (failuresSoFar == 1) {
//Simulate enabling debug logs
System.out.println("Second attempt: enabled debug logging");
}
if (failuresSoFar == 2) {
System.out.println("Aborting repetition");
throw new TestAbortedException(
"Aborting remaining repetitions after " + failuresSoFar + " failures"
);
}
// Simulate a flaky operation
System.out.println("Performing flaky test");
assertTrue(false);
}
}
Output$ mvn test -Dtest=ProgressiveRetryTest [INFO] Scanning for projects... [INFO] [INFO] ------< com.logicbig.example:junit-5-repeated-test-failure-count >------ [INFO] Building junit-5-repeated-test-failure-count 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-repeated-test-failure-count --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-repeated-tests\junit-5-repeated-test-failure-count\src\main\resources [INFO] [INFO] --- compiler:3.11.0:compile (default-compile) @ junit-5-repeated-test-failure-count --- [INFO] No sources to compile [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-repeated-test-failure-count --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-repeated-tests\junit-5-repeated-test-failure-count\src\test\resources [INFO] [INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ junit-5-repeated-test-failure-count --- [INFO] Changes detected - recompiling the module! :source [WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent! [INFO] Compiling 1 source file with javac [debug target 17] to target\test-classes [INFO] [INFO] --- surefire:3.5.0:test (default-test) @ junit-5-repeated-test-failure-count --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- Current repetition: 1 Performing flaky test Current repetition: 2 Second attempt: enabled debug logging Performing flaky test Current repetition: 3 Aborting repetition Current repetition: 4 Aborting repetition Current repetition: 5 Aborting repetition [INFO] +--com.logicbig.example.ProgressiveRetryTest - 0.226 ss [INFO] | +-- [XX] flakyOperation(RepetitionInfo) repetition 1 of 5 - 0.105 ss [INFO] | +-- [XX] flakyOperation(RepetitionInfo) repetition 2 of 5 - 0.015 ss [INFO] | +-- [??] flakyOperation(RepetitionInfo) repetition 3 of 5 - 0 ss [INFO] | +-- [??] flakyOperation(RepetitionInfo) repetition 4 of 5 - 0 ss [INFO] | '-- [??] flakyOperation(RepetitionInfo) repetition 5 of 5 - 0 ss [INFO] [INFO] Results: [INFO] [ERROR] Failures: [ERROR] ProgressiveRetryTest.flakyOperation:30 expected: <true> but was: <false> [ERROR] ProgressiveRetryTest.flakyOperation:30 expected: <true> but was: <false> [INFO] [ERROR] Tests run: 5, Failures: 2, Errors: 0, Skipped: 3 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 7.504 s [INFO] Finished at: 2025-12-27T06:09:44+08:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.0:test (default-test) on project junit-5-repeated-test-failure-count: There are test failures. [ERROR] [ERROR] Please refer to D:\example-projects\junit-5\junit-5-repeated-tests\junit-5-repeated-test-failure-count\target\surefire-reports for the individual test results. [ERROR] Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream. [ERROR] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
The output shows that each @RepeatedTest repetition is executed as a separate invocation, while RepetitionInfo#getFailureCount() tracks the number of previous failed repetitions. The first run fails with a failure count of 0, the second run sees a failure count of 1 and enables additional diagnostics, and the third run sees a failure count of 2 and throws TestAbortedException. Although JUnit still calls the test method for repetitions 4 and 5, those invocations are immediately aborted and reported as skipped.
Example ProjectDependencies and Technologies Used: - junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
Version Compatibility: 5.10.0 - 6.0.1 Version compatibilities of junit-jupiter-engine with this example:
- 5.10.0
- 5.10.1
- 5.10.2
- 5.10.3
- 5.10.4
- 5.10.5
- 5.11.0
- 5.11.1
- 5.11.2
- 5.11.3
- 5.11.4
- 5.12.0
- 5.12.1
- 5.12.2
- 5.13.0
- 5.13.1
- 5.13.2
- 5.13.3
- 5.13.4
- 5.14.0
- 5.14.1
- 6.0.0
- 6.0.1
Versions in green have been tested.
- JDK 25
- Maven 3.9.11
|
|