Repeated Tests in JUnit: Running Tests Multiple Times

Illustration for Repeated Tests in JUnit: Running Tests Multiple Times
By Last updated:

In real-world applications, some scenarios require running the same test multiple times. For instance, stress testing, verifying flaky behavior, or ensuring deterministic outcomes across iterations. JUnit 5 makes this possible with the @RepeatedTest annotation, eliminating the need for loops inside test methods.

In this tutorial, we’ll cover repeated tests in JUnit 5, explore use cases, dive into examples with RepetitionInfo, and combine them with frameworks like Mockito and Testcontainers.


Why Repeated Tests Matter

  • Reliability: Validate stability of flaky logic.
  • Performance Testing: Run lightweight load simulations.
  • Randomized Inputs: Ensure consistent results across multiple runs.
  • Integration Testing: Verify container or API stability over several attempts.
  • CI/CD Pipelines: Catch intermittent failures early.

Think of repeated tests like rehearsals for a play — by running the same scene multiple times, you catch mistakes before the premiere.


Basic Example: @RepeatedTest

JUnit 5 provides @RepeatedTest for running a test multiple times.

import org.junit.jupiter.api.RepeatedTest;
import static org.junit.jupiter.api.Assertions.assertTrue;

class RepeatedTestExample {

    @RepeatedTest(5)
    void shouldRunFiveTimes() {
        assertTrue(Math.random() >= 0); // Always true, runs 5 times
    }
}

This test runs five times, with each repetition counted separately in reports.


Using RepetitionInfo for Context

JUnit 5 allows injecting RepetitionInfo for more control.

import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;

class RepetitionInfoTest {

    @RepeatedTest(3)
    void testWithRepetitionInfo(RepetitionInfo info) {
        System.out.println("Running repetition " + info.getCurrentRepetition() +
                " of " + info.getTotalRepetitions());
    }
}

Output:

Running repetition 1 of 3
Running repetition 2 of 3
Running repetition 3 of 3

Custom Display Names

You can customize how repetitions appear in test reports.

import org.junit.jupiter.api.RepeatedTest;

class CustomDisplayNameTest {

    @RepeatedTest(value = 3, name = "Run {currentRepetition} of {totalRepetitions}")
    void testWithCustomNames() {
        System.out.println("Executing test...");
    }
}

Report output:

Run 1 of 3 ✔
Run 2 of 3 ✔
Run 3 of 3 ✔

Combining Repeated Tests with Assertions

import org.junit.jupiter.api.RepeatedTest;
import static org.junit.jupiter.api.Assertions.assertEquals;

class CalculatorTest {

    @RepeatedTest(5)
    void shouldAddNumbersCorrectly() {
        Calculator calc = new Calculator();
        assertEquals(5, calc.add(2, 3));
    }
}

Even though the logic doesn’t change, running it multiple times helps catch intermittent failures.


Repeated Tests with Mockito

import org.junit.jupiter.api.RepeatedTest;
import org.mockito.Mockito;

class OrderServiceTest {

    @RepeatedTest(3)
    void shouldVerifyMockMultipleTimes() {
        OrderRepository repo = Mockito.mock(OrderRepository.class);
        OrderService service = new OrderService(repo);

        service.placeOrder(new Order("Book"));
        Mockito.verify(repo).save(Mockito.any(Order.class));
    }
}

Mockito ensures the expected behavior is validated in every repetition.


Repeated Tests with Testcontainers

Repeated tests are useful when testing container startup consistency.

import org.junit.jupiter.api.RepeatedTest;
import org.testcontainers.containers.PostgreSQLContainer;

class DatabaseContainerTest {

    @RepeatedTest(2)
    void shouldStartPostgresMultipleTimes() {
        try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
            postgres.start();
            System.out.println("Postgres running on: " + postgres.getJdbcUrl());
            assert(postgres.isRunning());
        }
    }
}

Output (each repetition starts/stops a container):

Creating container for image: postgres:15
Container started in 11.5s

Real-World Scenarios

  1. Stress Testing APIs: Call endpoints multiple times to detect rate-limiting issues.
  2. Flaky Test Debugging: Run tests repeatedly to catch intermittent failures.
  3. Database Initialization: Validate Testcontainers setups across repetitions.
  4. Legacy Systems: Ensure consistency in fragile, unpredictable code.

Best Practices

  • Use repeated tests strategically, not as a fix for flaky tests.
  • Always log repetition details for debugging.
  • Avoid excessive repetitions in CI pipelines (they increase build times).
  • Combine with @Tag to separate fast vs slow repeated tests.
  • Use RepetitionInfo to apply conditional logic per iteration.

Version Tracker

  • JUnit 4 → JUnit 5: Repeated tests introduced in JUnit 5 (@RepeatedTest).
  • Mockito Updates: Inline mocking improves reliability of repeated test scenarios.
  • Testcontainers Growth: Expanded ecosystem makes repeated container tests viable.

Conclusion & Key Takeaways

Repeated tests in JUnit 5 provide a clean, built-in way to run tests multiple times. Whether for debugging flaky logic, testing containers, or stress-testing APIs, @RepeatedTest adds flexibility to modern Java test suites.

Key Takeaways:

  • Use @RepeatedTest for multiple executions.
  • Leverage RepetitionInfo for iteration context.
  • Combine with Mockito and Testcontainers for real-world scenarios.
  • Apply strategically for debugging and validation.

FAQ

1. What is the difference between @Test and @RepeatedTest?
@Test runs once, while @RepeatedTest executes multiple times.

2. Can I use @BeforeEach with repeated tests?
Yes, setup logic runs before each repetition.

3. How do I name repeated test executions?
Use the name attribute with placeholders like {currentRepetition}.

4. Do repeated tests slow down CI/CD pipelines?
Yes, use wisely to avoid long build times.

5. Can I repeat parameterized tests?
Yes, @RepeatedTest works with parameter injection as well.

6. How are repeated tests reported in IDEs?
Each repetition shows as a separate execution in reports.

7. Should I use repeated tests for flaky test fixes?
No, fix root causes instead — use repetitions for debugging or stress testing.

8. Can I use assumptions in repeated tests?
Yes, assumptions apply per repetition.

9. Do repeated tests work with Testcontainers?
Yes, containers can be started/stopped per repetition.

10. Should I migrate to JUnit 5 for repeated tests?
Yes, @RepeatedTest is a JUnit 5 feature not available in JUnit 4.