How to wait in Java with Awaitility

Testing asynchronous operations—such as background events, message queues, or reactive microservice endpoints—often leads developers to insert unreliable Thread.sleep() pauses. Awaitility is a lightweight Java DSL designed to replace fixed delays with fluent, non-blocking polling conditions. In this updated guide, you will learn how to use Awaitility with JUnit 5, AssertJ, and Spring Boot or Quarkus asynchronous applications.

Waiting in Java properly

In order to make a pause during the execution of Java code you can use two main approaches:

Java’s Thread.sleep() method is a built-in method that allows a thread to pause execution for a specified amount of time. It is simple to use and can be integrated easily into any Java code. However, it is not very flexible as it can only pause execution for a fixed amount of time, and it does not provide any way to check if a certain condition has been met before continuing execution.

Awaitility is a third-party library that provides a more flexible and powerful way to handle thread pauses and waiting for conditions in Java. It provides a variety of methods for waiting for specific conditions, such as waiting for a method to return a certain value or for a specific exception to be thrown. Additionally, Awaitility allows for more fine-grained control over the waiting process, such as setting a timeout for the wait and polling the condition at a specific interval.

Here are the advantages and disadvantages of each solution:

Pros of Thread.sleep():

  • Simple to use
  • Built-in to Java

Cons of Thread.sleep():

  • Only allows for pausing execution for a fixed amount of time
  • No way to check if a certain condition has been met before continuing execution

Pros of Awaitility:

  • More flexible and powerful way to handle thread pauses and waiting for conditions
  • Provides various methods for waiting for specific conditions
  • Allows for more fine-grained control over the waiting process

Cons of Awaitility:

  • Third-party library, not built-in to Java

Testing applications with Awaitility

Awaitility integrates seamlessly with JUnit 5, Testcontainers, and Spring Boot Test, making it a valuable tool in your testing toolbox when verifying async operations such as message consumption from Kafka, REST API readiness, or database state changes. For example, you can combine Testcontainers and Awaitility to wait for your PostgreSQL or Kafka containers to become ready before running your integration tests, eliminating race conditions in your CI pipelines.

For example, here is a basic sample:

await().atMost(5, SECONDS).until(checkCondition());

In the above example, checkCondition contains the condition to verify. Therefore, we will wait at most 5 seconds to verify that condition is true. If you don’t specify any timeout Awaitility will wait for 10 seconds and then throw a ConditionTimeoutException if the condition has not been fulfilled.

Even in this simple form, Awaitility has some advantages over Thread.sleep(). Indeed we don’t have to wait for a fixed amount of time if the condition is already verified.

Let’s see a slightly more complex example. In this example, we are introducing untilAsserted which waits until a Runnable supplier execution passes. In this example, we are asserting that in, at most 3 seconds, the server log file will contain a text in it:

await().atMost(3, TimeUnit.SECONDS).untilAsserted(() -> {
      String content =null;
      try {
    	  Path filePath = Path.of("/home/jboss/wildfly-26.1.0.Final/standalone/log/server.log");
    	  content = Files.readString(filePath);    
      } catch (IOException exc) { exc.printStackTrace(); }
      
      Assertions.assertTrue(content.indexOf("Admin console listening") > -1);
             
});

If your tests include several checks from , it is a good idea to introduce an alias for it. This way you will be able to catch easily which test is failing:

await("Check my Heros").atMost(3, SECONDS).until(herosAvailable());

Checking Types

Next, let’s see how to verify an assertion which requires checking a time limit and the returned Type, including its size.

Here is an Endpoint which produces an array of Hero objects:

@GET
public Hero[] get() {
        Hero hero[] = new Hero[3];
        hero[0] = new Hero("Spiderman");
        hero[1] = new Hero("Batman");
        hero[2] = new Hero("Hulk");
        return hero;
}

The following condition will check that, within 2 seconds, the HTTP GET endpoint returns an array of Hero with a lenght of 3:

await().atMost(2, SECONDS)
     .untilAsserted(() -> Assertions.assertEquals(3, get().as(Hero[].class).length));

Using Poll intervals

A poll interval tells Awaitility how often it should evaluate the condition which is in the until method. In the following example, we use a poll interval of 250 milliseconds with an initial delay of 50 milliseconds until

with().pollInterval(250, TimeUnit.MILLISECONDS).and().with().pollDelay(50, MILLISECONDS).await().until(herosAvailable());

Check the condition between a time frame

You can also check that the await condition is true only between a specific timing. For this purpose, you can use atLeast in your condition. For example:

await().atLeast(50, TimeUnit.MILLISECONDS).and().atMost(3, SECONDS).until(herosAvailable());

In the above example, the condition will evaluate to true if the method herosAvailable takes between 50 ms and 3 seconds. If it evaluates to true before, you will see the following Exception:

Condition was evaluated in 10 milliseconds which is earlier than expected minimum timeout 50 milliseconds

Check condition during an amount of time

Finally, it’s possible to assert that a condition evaluates to true during a time frame. For example, if we were to check that the endpoint returns the same number of Hero during 800 to 1500 ms:

await().during(800, MILLISECONDS).atMost(1500, MILLISECONDS).untilAsserted(() -> Assertions.assertEquals(3, get().as(Hero[].class).length));

In the above example, there won’t be any Exception if the condition evaluates to true earlier than the time frame.

How to build projects using Awaitility

Finally, in order to build Test classes using Awaitility you need to include the following dependency in your pom.xml (updated June 2025) :

<dependency>
            <groupId>org.awaitility</groupId>
            <artifactId>awaitility</artifactId>
            <version>4.3.0</version>
            <scope>test</scope>
</dependency>

🌱 Alternative Approaches to Consider

  1. Project Reactor’s StepVerifier: If you are using reactive programming with Reactor, StepVerifier can be an alternative to Awaitility for testing reactive streams deterministically.
  2. AssertJ Awaitility: Awaitility’s integration with AssertJ provides a fluent API for assertions while waiting, making your tests expressive and easier to maintain.
  3. Spring Boot’s @SpringBootTest with @DynamicPropertySource: For readiness checks without Awaitility, you can leverage Spring Boot’s test lifecycle hooks for certain scenarios, although Awaitility remains superior for custom conditions.

Conclusion

Awaitility is a powerful and lightweight tool that makes testing asynchronous operations in Java, Spring Boot, Quarkus, and Jakarta EE applications easy and reliable. By allowing you to wait for specific conditions in a fluent and readable way, Awaitility helps you eliminate flaky tests and replace inefficient Thread.sleep() calls in your test suites. You can find the source code for this example on GitHub: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/awaitility

Frequently Asked Questions

What happens if the condition in Awaitility is satisfied before the timeout?
Awaitility evaluates conditions continuously at configured poll intervals and completes test execution immediately when the condition becomes true, avoiding wasteful fixed delays.

How do I handle exceptions thrown inside untilAsserted() closures?
untilAsserted automatically catches assertion exceptions (like AssertionError) thrown inside its lambda during polling iterations and only fails the test if the condition remains unsatisfied when the timeout expires.


Recommended Articles

Customize Arquillian Testing Framework with Custom Server Configuration

Learn how to use a custom standalone.xml file for Arquillian testing with Java and Cloud-Native technologies.

Master JUnit Assertions for Robust Java Unit Testing

Learn how to use JUnit assertions for robust unit testing in Java, including assertAll, assertEquals, and more.

Arquillian Integration Testing Framework: A Comprehensive Guide to WAR and JAR Deployments

Master the art of Arquillian integration testing with our in-depth guide. Learn how to create, deploy, and manage WAR and JAR files using the framework.

Arquillian Tutorial: Setting Up Jakarta EE Project with Maven

Learn how to set up an Arquillian project for Jakarta EE using Maven. Simplify integration testing with Java middleware.