Pradyumn
Pradyumn

Reputation: 430

JUnit 5, Test not failing despite @Timeout

I wonder what I am missing, but my JUnit 5 test with @Timeout is not failing when it should.

Here is my test class:

package tests;

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

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

import main.SomeClass;

class SomeUtilsTest {

    @Test
    @Timeout (5)
    void testSomething() {
        assertEquals (1, SomeClass.doSomething());
    }

}

And here is the class that I am testing:

package main;

public class SomeClass {

    public static Integer doSomething() {
        while (true);
    }

}

It should fail after five seconds, but no matter how long I wait, the test never fails; the program continues to "run", and I can only manually terminate it.

Upvotes: 4

Views: 2176

Answers (2)

chorandrey
chorandrey

Reputation: 21

For me, the code below was the solution. It seems like Timeout doesn't work as expected when a long running computation and the code responsible for halting this computation run in the same thread.

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Timeout.ThreadMode.SEPARATE_THREAD;

class SomeUtilsTest {

    @Test
    @Timeout(value = 3, unit = TimeUnit.SECONDS, threadMode = SEPARATE_THREAD)
    void testSomething() {
        assertEquals (1, SomeClass.doSomething());
    }
}

Upvotes: 2

laszlok
laszlok

Reputation: 2515

A test configured with the @Timeout annotation will fail if its execution time exceeds a given duration. If the timeout is exceeded, the thread executing the test is interrupted from another thread, and is then expected to complete.

In your case, the interrupt does nothing, as you aren't performing any blocking operations, so your test continues to run forever.

What you can do instead is use one of the assertTimeoutPreemptively methods - see the documentation. In your example:

assertTimeoutPreemptively(Duration.ofSeconds(5), SomeClass::doSomething);

Do be aware that this will use a separate thread to execute SomeClass::doSomething. It is therefore up to you to ensure that everything you need (for the actual computation in the separate thread, or for subsequent assertions in the main thread) is correctly visible.

Upvotes: 4

Related Questions