Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In this short tutorial, we’ll use JUnit5’s @Timeout annotation to set timeouts for unit tests in a declarative style. We’ll discuss different ways of using it, and then we’ll see how it interacts with @Pratemerized and @Nested tests.

2. The @Timeout Annotation

We can annotate a unit test with JUnit5’s @Timeout to specify the maximum number of seconds it can run for; if this value is exceeded, the test will fail with a java.util.concurrent.TimeoutException:

@Test
@Timeout(1)
void shouldFailAfterOneSecond() throws InterruptedException {
    Thread.sleep(10_000);
}

2.1. The Value and Unit Attributes

We’ve learned how to specify a test’s timeout by specifying the number of seconds after which it fails. However, we can leverage the value and unit attributes of the annotation to specify different units of measurement:

@Test
@Timeout(value = 2, unit = TimeUnit.MINUTES)
void shouldFailAfterTwoMinutes() throws InterruptedException {
    Thread.sleep(10_000);
}

2.2. The ThreadMode Attribute

Let’s assume we have a slow test and, therefore, a large timeout. To run this efficiently, we should run this test on a different thread, not blocking the other tests. To achieve this, we can use JUnit5’s parallel test execution.

On the other hand, the @Timeout annotation itself allows us to do it elegantly through its threadMode attribute:

@Test
@Timeout(value = 5, unit = TimeUnit.MINUTES, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
void shouldUseADifferentThread() throws InterruptedException {
    System.out.println(Thread.currentThread().getName());
    Thread.sleep(10_000);
}

We can check this by running the test and printing the current thread’s name; it should print something like “junit-timeout-thread-1“.

3. @Timeout‘s Target

As highlighted earlier, the @Timeout annotation can be conveniently applied to individual test methods. However, it’s also possible to specify a default timeout duration for each test from the entire class by placing the annotation at the class level. As a result, the tests that do not override the class-level timeout will fail if that value is exceeded:

@Timeout(5)
class TimeoutUnitTest {

    @Test
    @Timeout(1)
    void shouldFailAfterOneSecond() throws InterruptedException {
        Thread.sleep(10_000);
    }

    @Test
    void shouldFailAfterDefaultTimeoutOfFiveSeconds() throws InterruptedException {
        Thread.sleep(10_000);
    }
}

3.1. @Timeout and @Nested Tests

JUnit5’s @Nested annotation can create inner classes for unit tests. We can use this in combination with @Timeout. If the parent class defines a default timeout value, it will be used by the test from the inner class as well:

@Timeout(5)
class TimeoutUnitTest {

    @Nested
    class NestedClassWithoutTimeout {
        @Test
	void shouldFailAfterParentsDefaultTimeoutOfFiveSeconds() throws InterruptedException {
            Thread.sleep(10_000);
	}
    }
}

However, this value can be overridden either at the nested class level or the method level:

@Nested
@Timeout(3)
class NestedClassWithTimeout {

    @Test
    void shouldFailAfterNestedClassTimeoutOfThreeSeconds() throws InterruptedException {
        Thread.sleep(10_000);
    }

    @Test
    @Timeout(1)
    void shouldFailAfterOneSecond() throws InterruptedException {
        Thread.sleep(10_000);
    }
}

3.2. @Timeout and @ParameterizedTest

We can leverage the @ParameterizedTest annotation to execute multiple tests based on a given set of input values. We can annotate a parameterized test with @Timeout, and, as a result, each generated test will use the timeout value.

For instance, if we have a @ParameterizedTest that will execute five tests and we annotate it with @Timeout(1), each of the five resulting tests will fail if it exceeds one second:

@Timeout(1)
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void eachTestShouldFailAfterOneSecond(int input) throws InterruptedException {
    Thread.sleep(1100);
}

4. Conclusion

In this article, we discussed JUnit5’s new @Timeout annotation. We learned how to configure it and use it to set timeout values for our unit test in a declarative manner.

As always, the full source code for this article is available over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.