2.18. Timeouts

Declarative timeouts are an experimental feature
You’re invited to give it a try and provide feedback to the JUnit team so they can improve and eventually promote this feature.

The @Timeout annotation allows one to declare that a test, test factory, test template, or lifecycle method should fail if its execution time exceeds a given duration. The time unit for the duration defaults to seconds but is configurable.

The following example shows how @Timeout is applied to lifecycle and test methods.

  1. class TimeoutDemo {
  2. @BeforeEach
  3. @Timeout(5)
  4. void setUp() {
  5. // fails if execution time exceeds 5 seconds
  6. }
  7. @Test
  8. @Timeout(value = 100, unit = TimeUnit.MILLISECONDS)
  9. void failsIfExecutionTimeExceeds100Milliseconds() {
  10. // fails if execution time exceeds 100 milliseconds
  11. }
  12. }

Contrary to the assertTimeoutPreemptively() assertion, the execution of the annotated method proceeds in the main thread of the test. If the timeout is exceeded, the main thread is interrupted from another thread. This is done to ensure interoperability with frameworks such as Spring that make use of mechanisms that are sensitive to the currently running thread — for example, ThreadLocal transaction management.

To apply the same timeout to all test methods within a test class and all of its @Nested classes, you can declare the @Timeout annotation at the class level. It will then be applied to all test, test factory, and test template methods within that class and its @Nested classes unless overridden by a @Timeout annotation on a specific method or @Nested class. Please note that @Timeout annotations declared at the class level are not applied to lifecycle methods.

Declaring @Timeout on a @TestFactory method checks that the factory method returns within the specified duration but does not verify the execution time of each individual DynamicTest generated by the factory. Please use assertTimeout() or assertTimeoutPreemptively() for that purpose.

If @Timeout is present on a @TestTemplate method — for example, a @RepeatedTest or @ParameterizedTest — each invocation will have the given timeout applied to it.

The following configuration parameters can be used to specify global timeouts for all methods of a certain category unless they or an enclosing test class is annotated with @Timeout:

junit.jupiter.execution.timeout.default

Default timeout for all testable and lifecycle methods

junit.jupiter.execution.timeout.testable.method.default

Default timeout for all testable methods

junit.jupiter.execution.timeout.test.method.default

Default timeout for @Test methods

junit.jupiter.execution.timeout.testtemplate.method.default

Default timeout for @TestTemplate methods

junit.jupiter.execution.timeout.testfactory.method.default

Default timeout for @TestFactory methods

junit.jupiter.execution.timeout.lifecycle.method.default

Default timeout for all lifecycle methods

junit.jupiter.execution.timeout.beforeall.method.default

Default timeout for @BeforeAll methods

junit.jupiter.execution.timeout.beforeeach.method.default

Default timeout for @BeforeEach methods

junit.jupiter.execution.timeout.aftereach.method.default

Default timeout for @AfterEach methods

junit.jupiter.execution.timeout.afterall.method.default

Default timeout for @AfterAll methods

More specific configuration parameters override less specific ones. For example, junit.jupiter.execution.timeout.test.method.default overrides junit.jupiter.execution.timeout.testable.method.default which overrides junit.jupiter.execution.timeout.default.

The values of such configuration parameters must be in the following, case-insensitive format: <number> [ns|μs|ms|s|m|h|d]. The space between the number and the unit may be omitted. Specifying no unit is equivalent to using seconds.

Table 1. Example timeout configuration parameter values
Parameter valueEquivalent annotation

42

@Timeout(42)

42 ns

@Timeout(value = 42, unit = NANOSECONDS)

42 μs

@Timeout(value = 42, unit = MICROSECONDS)

42 ms

@Timeout(value = 42, unit = MILLISECONDS)

42 s

@Timeout(value = 42, unit = SECONDS)

42 m

@Timeout(value = 42, unit = MINUTES)

42 h

@Timeout(value = 42, unit = HOURS)

42 d

@Timeout(value = 42, unit = DAYS)

2.18.1. Using @Timeout for Polling Tests

When dealing with asynchronous code, it is common to write tests that poll while waiting for something to happen before performing any assertions. In some cases you can rewrite the logic to use a CountDownLatch or another synchronization mechanism, but sometimes that is not possible — for example, if the subject under test sends a message to a channel in an external message broker and assertions cannot be performed until the message has been successfully sent through the channel. Asynchronous tests like these require some form of timeout to ensure they don’t hang the test suite by executing indefinitely, as would be the case if an asynchronous message never gets successfully delivered.

By configuring a timeout for an asynchronous test that polls, you can ensure that the test does not execute indefinitely. The following example demonstrates how to achieve this with JUnit Jupiter’s @Timeout annotation. This technique can be used to implement “poll until” logic very easily.

  1. @Test
  2. @Timeout(5) // Poll at most 5 seconds
  3. void pollUntil() throws InterruptedException {
  4. while (asynchronousResultNotAvailable()) {
  5. Thread.sleep(250); // custom poll interval
  6. }
  7. // Obtain the asynchronous result and perform assertions
  8. }
If you need more control over polling intervals and greater flexibility with asynchronous tests, consider using a dedicated library such as Awaitility.

2.18.2. Disable @Timeout Globally

When stepping through your code in a debug session, a fixed timeout limit may influence the result of the test, e.g. mark the test as failed although all assertions were met.

JUnit Jupiter supports the junit.jupiter.execution.timeout.mode configuration parameter to configure when timeouts are applied. There are three modes: enabled, disabled, and disabled_on_debug. The default mode is enabled. A VM runtime is considered to run in debug mode when one of its input parameters starts with -agentlib:jdwp. This heuristic is queried by the disabled_on_debug mode.