Sometimes you need to make sure a method does not run forever. Maybe it calls an
external API, processes a large file, or does some computation that should not
take too long. JUnit 5's @Timeout annotation lets you set time limits
on your tests.
Basic @Timeout Usage
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.Test;
import java.util.concurrent.TimeUnit;
class TimeoutTest {
@Test
@Timeout(5) // 5 seconds
void shouldCompleteWithinFiveSeconds() {
// If this takes more than 5 seconds, test fails
int result = heavyComputation();
assertEquals(42, result);
}
@Test
@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
void shouldCompleteWithin500Millis() {
// More precise control
String result = quickOperation();
assertNotNull(result);
}
@Test
@Timeout(value = 1, unit = TimeUnit.MINUTES)
void shouldCompleteWithinOneMinute() {
// Long-running test with generous timeout
processData();
}
}
Timeout with assertTimeout
assertTimeout is an alternative that works inline:
import org.junit.jupiter.api.Test;
import java.time.Duration;
class InlineTimeoutTest {
@Test
void shouldCompleteWithinTimeout() {
assertTimeout(Duration.ofSeconds(2), () -> {
// This entire block must complete within 2 seconds
int result = compute();
assertEquals(100, result);
});
}
@Test
void shouldCompleteWithinMillis() {
assertTimeout(Duration.ofMillis(500), () -> {
quickTask();
});
}
}
@Timeout on Class Level
You can set a timeout for the entire test class:
@Test
@Timeout(value = 30, unit = TimeUnit.SECONDS)
class AllTestsMustBeFastTest {
@Test
void test1() { /* ... */ }
@Test
void test2() { /* ... */ }
@Test
void test3() { /* ... */ }
}
If the total execution time of all tests exceeds 30 seconds, the class-level timeout kicks in.
Timeout Modes
@Timeout has two modes:
// FAIL mode (default) - test fails if timeout exceeded
@Test
@Timeout(value = 5, unit = TimeUnit.SECONDS)
void failsOnTimeout() {
// ...
}
// INTERRUPT mode - thread is interrupted on timeout
@Test
@Timeout(value = 5, unit = TimeUnit.SECONDS, threadMode =
Timeout.ThreadMode.SEPARATE_THREAD)
void interruptsOnTimeout() {
// Runs in a separate thread, interrupted on timeout
}
When to Use Timeouts
- Testing methods that call external services
- Ensuring algorithms complete in reasonable time
- Preventing tests from hanging indefinitely
- Performance regression tests
- Verifying async operations complete
@Test
@Timeout(value = 10, unit = TimeUnit.SECONDS)
void shouldProcessLargeDataset() {
DataSet data = loadLargeDataSet();
Result result = processor.process(data);
assertNotNull(result);
assertTrue(result.isComplete());
}
@Test
void shouldCallExternalApi() {
assertTimeout(Duration.ofSeconds(5), () -> {
Response response = apiClient.getUsers();
assertEquals(200, response.getStatusCode());
});
}
Best Practices
- Set realistic timeouts โ do not make them too tight
- Use timeouts mainly for integration or external service tests
- Unit tests should be fast enough to not need timeouts
- Consider using
SEPARATE_THREADmode for reliable timeout enforcement - Combine with
@Tag("slow")for tests that naturally take longer