148

I have a JUnit test that I want to wait for a period of time synchronously. My JUnit test looks like this:

@Test
public void testExipres(){
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);
    // WAIT FOR 2 SECONDS
    assertNull(sco.getIfNotExipred("foo"));
}

I tried Thread.currentThread().wait(), but it throws an IllegalMonitorStateException (as expected).

Is there some trick to it or do I need a different monitor?

1

10 Answers 10

132

How about Thread.sleep(2000); ? :)

5
  • 45
    If you are using code analysis tools like sonarqube they will complain about Thread.sleep saying something like Using Thread.sleep in a test is just generally a bad idea. It creates brittle tests that can fail unpredictably depending on environment ("Passes on my machine!") or load. Don't rely on timing (use mocks) or use libraries such as Awaitility for asynchroneous testing.
    – FuryFart
    Commented Mar 14, 2019 at 15:07
  • 22
    This answer should be removed, and considered harmful. A much better answer below is stackoverflow.com/a/35163873/1229735
    – yiati
    Commented Apr 6, 2020 at 16:31
  • To avoid the "Thread.sleep should not be used in tests" sonar code smell, use the solution in this answer stackoverflow.com/a/51650781/1167954
    – heyomi
    Commented Jul 9, 2021 at 11:51
  • Thread.sleep() is not recommended by SonarQube. This answer does the job but it is misleading according to the coding standards. Commented Oct 14, 2021 at 4:17
  • This is a correct answer. OP never asked about SonarQube, so not seeing how that matters here.
    – breakline
    Commented Feb 21, 2022 at 12:00
120

Thread.sleep() could work in most cases, but usually if you're waiting, you are actually waiting for a particular condition or state to occur. Thread.sleep() does not guarantee that whatever you're waiting for has actually happened.

If you are waiting on a rest request for example maybe it usually return in 5 seconds, but if you set your sleep for 5 seconds the day your request comes back in 10 seconds your test is going to fail.

To remedy this JayWay has a great utility called Awatility which is perfect for ensuring that a specific condition occurs before you move on.

It has a nice fluent api as well

await().until(() -> 
{
    return yourConditionIsMet();
});  

https://github.com/jayway/awaitility

4
  • 1
    I couldn't get awaitability to compile in Android Studio. Commented Sep 30, 2016 at 15:42
  • Did you add this line to you gradle file: compile 'org.awaitility:awaitility:3.0.0' ?
    – Samoht
    Commented Nov 27, 2017 at 10:10
  • no. you would add this in a test case where you want to wait for a particular condition Commented Dec 2, 2017 at 13:56
  • This is such a brilliant utility! Also there is a kotlin dsl.
    – astroboy
    Commented Mar 6, 2021 at 7:32
40

In case your static code analyzer (like SonarQube) complaints, but you can not think of another way, rather than sleep, you may try with a hack like: Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true); It's conceptually incorrect, but it is the same as Thread.sleep(1000).

The best way, of course, is to pass a Callable, with your appropriate condition, rather than true, which I have.

https://github.com/awaitility/awaitility

2
  • @Jitendra: Note that Awaitility used to have the Duration class, but since version 4 they renamed it to Durations. Commented Sep 27, 2019 at 6:54
  • 2
    One caveat, if you want the poll delay to be 10 seconds or more, be sure to increase the timeout to more than 10 seconds since that is the default timeout and awaitility will complain that timeout is less than the poll delay.
    – focus
    Commented Oct 17, 2019 at 22:52
32

You can use java.util.concurrent.TimeUnit library which internally uses Thread.sleep. The syntax should look like this :

@Test
public void testExipres() throws InterruptedException {
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);
    
    TimeUnit.SECONDS.sleep(2);
   
    assertNull(sco.getIfNotExipred("foo"));
}

This library provides more clear interpretation for time unit. You can use 'HOURS'/'MINUTES'/'SECONDS'.

4
  • If I start a worker thread from within a test, will the sleep() affects the worker thread? Commented May 27, 2020 at 9:27
  • 1
    Sonar doesn't complain about TimeUnit.SECONDS.sleep(2); but the underlying problem is the same: you're blocking the thread and it might behave differently on your test server ("but worked on my machine" (TM)) Commented Jul 27, 2021 at 15:06
  • 1
    This code does not compile. TimeUnit.sleep (like Thread.sleep) declares that it throws InterruptedException, but testExipres does not catch it, nor does it declare that it throws InterruptedException. Commented Apr 25, 2022 at 12:06
  • I just corrected the answer so the bug mentioned by @RichardFearn is now fixed. Commented Apr 22 at 7:40
23

If it is an absolute must to generate delay in a test CountDownLatch is a simple solution. In your test class declare:

private final CountDownLatch waiter = new CountDownLatch(1);

and in the test where needed:

waiter.await(1000 * 1000, TimeUnit.NANOSECONDS); // 1ms

Maybe unnecessary to say but keeping in mind that you should keep wait times small and not cumulate waits to too many places.

5
  • IntelliJ unfortunately complains because the result of await is ignored.
    – Druckles
    Commented Dec 16, 2021 at 18:44
  • @Druckles are you making an assignment like var result = waiter.await(..? In that case do not make it. If not doing it then the warning is weird. There could be some "checkstyle" rule that require always an assignment but hardly is.
    – pirho
    Commented Dec 16, 2021 at 19:12
  • @pirho Can I invoke waiter.await() multiple times within a test?
    – linuxNoob
    Commented Dec 22, 2021 at 18:47
  • 1
    @linuxNoob Why not. For me even Junit5 parallel test run seems to work as should for the same class level static CountDownLatch when I use it several times in two separate tests.
    – pirho
    Commented Dec 23, 2021 at 15:36
  • @Druckles the following trick has worked for me to avoid the warning assertThat(waiter.await(5, TimeUnit.SECONDS), equalTo(false))
    – Sergey Ch
    Commented Jun 6, 2023 at 16:21
12

Mockito (which is already provided via transitive dependencies for Spring Boot projects) has a couple of ways to wait for asynchronous events, respectively conditions to happen.

A simple pattern which currently works very well for us is:

// ARRANGE – instantiate Mocks, setup test conditions

// ACT – the action to test, followed by:
Mockito.verify(myMockOrSpy, timeout(5000).atLeastOnce()).delayedStuff();
// further execution paused until `delayedStuff()` is called – or fails after timeout

// ASSERT – assertThat(...)

Two slightly more complex yet more sophisticated are described in this article by @fernando-cejas


My urgent advice regarding the current top answers given here: you want your tests to

  1. finish as fast as possible
  2. have consistent results, independent of the test environment (non-"flaky")

... so just don't be silly by using Thread.sleep() in your test code.

Instead, have your production code use dependency injection (or, a little "dirtier", expose some mockable/spyable methods) then use Mockito, Awaitly, ConcurrentUnit or others to ensure asynchronous preconditions are met before assertions happen.

1
  • 1
    OMG, how I lived before that for 10 years
    – borino
    Commented Jun 16, 2022 at 16:50
7

You could also use the CountDownLatch object like explained here.

4

There is a general problem: it's hard to mock time. Also, it's really bad practice to place long running/waiting code in a unit test.

So, for making a scheduling API testable, I used an interface with a real and a mock implementation like this:

public interface Clock {
    
    public long getCurrentMillis();
    
    public void sleep(long millis) throws InterruptedException;
    
}

public static class SystemClock implements Clock {

    @Override
    public long getCurrentMillis() {
        return System.currentTimeMillis();
    }

    @Override
    public void sleep(long millis) throws InterruptedException {
        Thread.sleep(millis);
    }
    
}

public static class MockClock implements Clock {

    private final AtomicLong currentTime = new AtomicLong(0);
    

    public MockClock() {
        this(System.currentTimeMillis());
    }
    
    public MockClock(long currentTime) {
        this.currentTime.set(currentTime);
    }
    

    @Override
    public long getCurrentMillis() {
        return currentTime.addAndGet(5);
    }

    @Override
    public void sleep(long millis) {
        currentTime.addAndGet(millis);
    }
    
}

With this, you could imitate time in your test:

@Test
public void testExpiration() {
    MockClock clock = new MockClock();
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExpiration("foo", 1000);
    clock.sleep(2000) // wait for 2 seconds
    assertNull(sco.getIfNotExpired("foo"));
}

An advanced multi-threading mock for Clock is much more complex, of course, but you can make it with ThreadLocal references and a good time synchronization strategy, for example.

3

Using Thread.sleep in a test is not a good practice. It creates brittle tests that can fail unpredictably depending on environment ("Passes on my machine!") or load. Don’t rely on timing (use mocks) or use libraries such as Awaitility for asynchroneous testing.

Dependency : testImplementation 'org.awaitility:awaitility:3.0.0'

await().pollInterval(Duration.FIVE_SECONDS).atLeast(Duration.FIVE_SECONDS).atMost(Duration.FIVE_SECONDS).untilAsserted(() -> {
      // your assertion
    });
0
-1

Use the Awaitility library.

Maven Dependency:

<dependency>
  <groupId>org.awaitility</groupId>
  <artifactId>awaitility</artifactId>
  <version>${awaitility.version}</version>
  <scope>test</scope>
</dependency>

Unit test utility method to wait for some specified amount of time:

 public void waitFor(Duration duration) {
    final Instant startTime = Instant.now();
    Awaitility
      .await()
      .atMost(duration.plus(Duration.ofSeconds(1)))
      .with()
      .pollInterval(duration)
      .until(() -> {
        Duration elapsed = Duration.between(Instant.now(), startTime);
        return elapsed.abs().compareTo(duration) >= 0;
      });
  }

Example usage based on OP:

@Test
public void testExipres(){
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);
    Duration pt2s = java.time.Duration.ofSeconds(2);
    waitFor(pt2s);
    assertNull(sco.getIfNotExipred("foo"));
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.