Skip to content

Awaitility

TestingTesting/AsyncJava

What it is

Awaitility is a Java DSL (Domain Specific Language) for testing asynchronous code. It allows developers to wait for a certain condition to be met within a specified time period, making testing of async operations, background tasks, or event-driven systems more reliable and readable.

Awaitility allows you to define expectations using conditions, durations, and polling intervals. It integrates with JUnit, TestNG, and other test frameworks, making async testing straightforward.

Installation

Add dependency in pom.xml: <dependency> <groupId>org.awaitility</groupId> <artifactId>awaitility</artifactId> <version>4.2.0</version> <scope>test</scope> </dependency>

Getting started

The smallest useful thing you can do with it, and what each part means.

Waiting for a flag to be true
import static org.awaitility.Awaitility.await;
import java.util.concurrent.TimeUnit;

boolean[] flag = {false};
new Thread(() -> {
    Thread.sleep(1000);
    flag[0] = true;
}).start();

await().atMost(5, TimeUnit.SECONDS).until(() -> flag[0]);
Waits up to 5 seconds until the `flag` variable becomes true, allowing asynchronous threads to complete.
Waiting for a value to change
import static org.awaitility.Awaitility.await;
import java.util.concurrent.atomic.AtomicInteger;

AtomicInteger counter = new AtomicInteger(0);
new Thread(() -> counter.incrementAndGet()).start();
await().untilAtomic(counter, val -> val > 0);
Waits for an AtomicInteger to become greater than 0.

Advanced usage

Where the library earns its place over a simpler alternative.

Custom polling interval
await().atMost(10, TimeUnit.SECONDS).pollInterval(500, TimeUnit.MILLISECONDS)
       .until(() -> myService.isReady());
Polls every 500 milliseconds for a condition to become true, with a maximum timeout of 10 seconds.
Ignoring exceptions
await().atMost(5, TimeUnit.SECONDS).ignoreExceptions()
       .until(() -> externalService.getData() != null);
Ignores transient exceptions while waiting for a condition, useful for unstable or delayed async services.
Combining with matchers
import static org.hamcrest.Matchers.*;
await().atMost(3, TimeUnit.SECONDS).until(() -> list.size(), equalTo(5));
Uses Hamcrest matchers to define more expressive conditions.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

ConditionTimeoutException
Occurs when the expected condition is not met within the specified timeout. Verify the async logic or increase the timeout duration if necessary.
IllegalStateException
Occurs if Awaitility is misconfigured, e.g., negative timeouts or invalid poll intervals. Ensure proper API usage.

Best practices

  • Avoid using Thread.sleep() in tests; prefer Awaitility for readability and reliability.
  • Define reasonable timeouts to prevent tests from hanging indefinitely.
  • Use atomic variables or thread-safe constructs for shared state in async tests.
  • Integrate with JUnit or TestNG to run async tests seamlessly.
  • Use polling intervals to balance responsiveness and CPU usage during waits.

Background

Why it exists, and what it was reacting to.

Testing asynchronous operations in Java can be challenging due to timing and concurrency issues. Awaitility was created to simplify this by providing a clean, readable API to wait for conditions without using Thread.sleep() or complex polling logic. It is widely used in unit tests, integration tests, and reactive systems testing.