Skip to content
Johan Haleby edited this page Mar 15, 2024 · 130 revisions

This is the documentation for Awaitility 4.x and above. For version 3.x and below please refer to this documentation instead.


For Scala documentation click here.
For Groovy documentation click here.
For Kotlin documentation click here.


Contents

  1. Static imports
  2. Usage examples
    1. Simple
    2. Reusing Conditions
    3. Fields
    4. Atomic, Adders and Accumulators
    5. Advanced
    6. Lambdas
    7. Using AssertJ or Fest Assert
    8. Ignoring Exceptions
    9. Checked exceptions in Runnable lambda expressions
    10. At Least
    11. Ignoring uncaught exceptions
    12. Assert that a value is maintained (during)
  3. Thread Handling
  4. Exception handling
  5. Deadlock detection
  6. Defaults
  7. Polling
    1. Fixed
    2. Fibonacci
    3. Iterative
    4. Custom
  8. Condition Evaluation Listener
  9. Fail-Fast Conditions
  10. Important
  11. Links and code examples

Static imports

In order to use Awaitility effectively it's recommended to statically import the following methods from the Awaitility framework:

  • org.awaitility.Awaitility.*

It may also be useful to import these methods:

  • java.time.Duration.*
  • java.util.concurrent.TimeUnit.*
  • org.hamcrest.Matchers.*
  • org.junit.Assert.*

Usage examples

Simple

Let's assume that we send a "add user" message to our asynchronous system like this:

publish(new AddUserMessage("Awaitility Rocks"));

In your test case Awaitility can help you to easily verify that the database has been updated. In its simplest form it may look something like this:

await().until(newUserIsAdded());

newUserIsAdded is a method that you implement yourself in your test case. It specifies the condition that must be fulfilled in order for Awaitility to stop waiting.

private Callable<Boolean> newUserIsAdded() {
	return () -> userRepository.size() == 1; // The condition that must be fulfilled
}

You can of course also inline the newUserIsAdded method and just write:

await().until(() -> userRepository.size() == 1);

By default Awaitility will wait for 10 seconds and if the size of the user repository is not equal to 1 during this time it'll throw a ConditionTimeoutException failing the test. If you want a different timeout you can define it like this:

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

Reusing Conditions

Awaitility also supports splitting up the condition into a supplying and a matching part for better reuse. The example above can also be written as:

await().until( userRepositorySize(), equalTo(1) );

The userRepositorySize method is now a Callable of type Integer:

private Callable<Integer> userRepositorySize() {
      return () -> userRepository.size(); // The suppling part of the condition
}

equalTo is a standard Hamcrest matcher specifying the matching part of the condition for Awaitility.

Now we could reuse the userRepositorySize in a different test. E.g. let's say we have a test that adds three users at the same time:

publish(new AddUserMessage("User 1"), new AddUserMessage("User 2"), new AddUserMessage("User 3"));

We now reuse the userRepositorySize "condition supplier" and simply update the Hamcrest matcher:

await().until( userRepositorySize(), equalTo(3) );

In simple cases like this you could of course also make use of Java method references:

await().until(userRepository::size, equalTo(3) );

Fields

You can also build the suppling part by referring to a field. E.g:

await().until( fieldIn(object).ofType(int.class), equalTo(2) );

or:

await().until( fieldIn(object).ofType(int.class).andWithName("fieldName"), equalTo(2) );

or:

await().until( fieldIn(object).ofType(int.class).andAnnotatedWith(MyAnnotation.class), equalTo(2) );

Atomic, Adders and Accumulators

If you're using Atomic structures Awaitility provides a simple way to wait until they match a specific value:

AtomicInteger atomic = new AtomicInteger(0);
// Do some async stuff that eventually updates the atomic integer
await().untilAtomic(atomic, equalTo(1));

Waiting for an AtomicBoolean is even simpler:

AtomicBoolean atomic = new AtomicBoolean(false);
// Do some async stuff that eventually updates the atomic boolean
await().untilTrue(atomic);

If you're using Adders, such as LongAdder, Awaitility allows you to simply wait for it to reach a certain value:

await().untilAdder(myLongAdder, equalTo(5L))

Likewise, if using Accumulators such as LongAccumulator, you can do:

await().untilAccumulator(myLongAccumulator, equalTo(5L))

Advanced

Use a poll interval of 100 milliseconds with an initial delay of 20 milliseconds until customer status is equal to "REGISTERED". This example also uses a named await by specifying an alias ("customer registration"). This makes it easy to find out which await statement that failed if you have multiple awaits in the same test.

with().pollInterval(ONE_HUNDERED_MILLISECONDS).and().with().pollDelay(20, MILLISECONDS).await("customer registration").until(
            customerStatus(), equalTo(REGISTERED));

You can also specify an alias like this:

await().with().alias("my alias"). ..

To use a non-fixed poll interval refer to the poll interval documentation.

Lambdas

You can use lambda expressions in your conditions:

await().atMost(5, SECONDS).until(() -> userRepository.size() == 1);

Or method references:

await().atMost(5, SECONDS).until(userRepository::isNotEmpty);

Or a combination of method references and Hamcrest matchers:

await().atMost(5, SECONDS).until(userRepository::size, is(1));

You can also use a predicate:

await().atMost(5, SECONDS).until(userRepository::size, size -> size == 1);

For examples refer to the Jayway team blog.

Using AssertJ or Fest Assert

You can use AssertJ or Fest Assert as an alternative to Hamcrest (it's actually possible to use any third party library that throws exception on error).

await().atMost(5, SECONDS).untilAsserted(() -> assertThat(fakeRepository.getValue()).isEqualTo(1));

Ignoring Exceptions

It's sometimes useful to ignore certain types of exceptions during condition evaluation. For example if you're waiting for something that throws exceptions as an intermediate state before the final state is reached. As an example take Spring's SocketUtils class that allows you to find an TCP port in a given range. It will throw an exception if no port is available in the given range. So let's say we know that some ports in a given range are not available but we want to wait for them to be so. This is an example where we may choose to ignore any exception thrown by SocketUtils. For example:

given().ignoreExceptions().await().until(() -> SocketUtils.findAvailableTcpPort(x,y));

This instruct Awaitility to ignore all caught exceptions during condition evaluation. Exceptions will be treated as evaluating to false. The test will not fail (unless it times out) upon an exception matching the supplied exception type. You can also ignore specific exceptions:

given().ignoreException(IllegalStateException.class).await().until(() -> SocketUtils.findAvailableTcpPort(x,y));

or using a Hamcrest matcher:

given().ignoreExceptionsMatching(instanceOf(RuntimeException.class)).await().until(() -> SocketUtils.findAvailableTcpPort(x,y));

or a predicate (Java 8):

given().ignoreExceptionsMatching(e -> e.getMessage().startsWith("Could not find an available")).await().until(something());

You can also ignore instances of Throwable.

Checked exceptions in Runnable lambda expressions

A Runnable interface in Java doesn't allow you to throw checked exceptions. So if you have a method like this:

public void waitUntilCompleted() throws Exception { ... }

that may throw an exception, you are forced you catch the exception if untilAsserted took a Runnable as its parameter value:

await().untilAsserted(() -> {
   try {
      waitUntilCompleted();
   } catch(Exception e) {
     // Handle exception
   }
});

Luckily Awaitility gets around this by introducing a ThrowingRunnable interface that you pass to untilAsserted instead of a Runnable. So the code you need to write simply looks like this:

await().untilAsserted(() -> waitUntilCompleted());

At Least

You can instruct Awaitility wait at least a certain amount of time. For example:

await().atLeast(1, SECONDS).and().atMost(2, SECONDS).until(value(), equalTo(1));

In case the condition is fulfilled before the duration specified by atLeast an exception is thrown indicating that the condition shouldn't be completed earlier than the specified duration.

Ignoring uncaught exceptions

If you're migrating code from using Thread.sleep(..) to Awaitility beware that in some cases the Awaitility test cases might fail due to exceptions being thrown from other threads. This is because the Awaitility catches all uncaught exceptions by default. So if you previously used Thread.sleep(..) then chances are that you didn't catch exceptions from other threads. If you're happy with this behavior and don't want Awaitility to catch these exceptions you can disable this functionality by using dontCatchUncaughtExceptions:

@Test
public void dontCatchUncaughtExample() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setMaxPoolSize(1);
    executor.afterPropertiesSet();

    executor.execute(new ErrorTestTask());
    ListenableFuture<?> future = executor.submitListenable(new ErrorTestTask());

    Awaitility.await()
              .dontCatchUncaughtExceptions()
              .atMost(1, TimeUnit.SECONDS)
              .pollInterval(10, TimeUnit.MILLISECONDS)
              .until(future::isDone);
}

private static class ErrorTestTask implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        throw new RuntimeException(Thread.currentThread().getName() + " -> Error");
    }
}

Assert that a value is maintained

As of Awaitility 4.0.2 it's possible to assert that a value is maintained during a specific period of time. For example if you need to make sure that a value in a repository maintains a specific value during 800 out of 1500 milliseconds:

await().during(800, MILLISECONDS).atMost(1500, MILLISECONDS).until(() -> myRepository.findById("id"), equalTo("something"));

Awaitility will wait for at most 1500 milliseconds and while doing so myRepository.findById("id") must be equal to "something" for at least 800 milliseconds.

Thread Handling

Awaitility allows for fine-grained thread configuration. This is done by suppling a Thread (supplier) or ExecutorService that will be used when Awaility polls the condition. Note that this is an advanced feature and should be used sparingly. For example:

given().pollThread(Thread::new).await().atMost(1000, MILLISECONDS).until(..);

another way is to specify an ExecutorService:

ExecutorSerivce es = ...
given().pollExecutorService(es).await().atMost(1000, MILLISECONDS).until(..);

This is useful for example if you need to wait for conditions that poll a ThreadLocal variable.

In some situations it's important to be able to instruct Awaitility to use the same thread as the test case that starts Awaitility. For this reason Awaitility 3.0.0 introduced the pollInSameThread method:

with().pollInSameThread().await().atMost(1000, MILLISECONDS).until(...);

This is an advanced feature and you should be careful when combining "pollInSameThread" with conditions that wait forever (or a long time) since Awaitility cannot interrupt the thread when it's using the same thread as the test.

Exception handling

By default Awaitility catches uncaught throwables in all threads and propagates them to the awaiting thread. This means that your test-case will indicate failure even if it's not the test-thread that threw the uncaught exception.

You can choose to ignore certain exceptions or throwables, see here.

If you don't need to catch exceptions in all threads you can use dontCatchUncaughtExceptions.

Deadlock detection

Awaitility automatically detects deadlocks and associates the cause of the ConditionTimeoutException with a DeadlockException. The DeadlockException contains information on what threads are causing the deadlock.

Defaults

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. The default poll interval and poll delay is 100 milliseconds. You can also specify the default values yourself using:

  Awaitility.setDefaultTimeout(..)
  Awaitility.setDefaultPollInterval(..)
  Awaitility.setDefaultPollDelay(..)

You can also reset back to the default values using Awaitility.reset().

Polling

Note that since Awaitility uses polling to verify that a condition matches it's not recommended to use it for precise performance testing. In these cases it's better to use an AOP framework such as AspectJ's compile-time weaving.

Also note that Duration.ZERO is used as start value for all non-fixed poll intervals. For fixed poll intervals the poll delay is equal to the duration of the FixedPollInterval for backward compatibility reasons.

Have a look at this blog for additional details.

Fixed Poll Interval

This is the default poll interval mechanism of Awaitilty. When using the DSL in the normal way such as:

with().pollDelay(100, MILLISECONDS).and().pollInterval(200, MILLISECONDS).await().until(<condition>);

Awaitility will use a FixedPollInterval. This means that Awaitility will check if the specified condition is fulfilled for the first time after a "poll delay" (the initial delay before the polling begins, 100ms in the example above). Unless explicitly specified, Awaitility will use the same poll delay as poll interval (note that this is only true of fixed poll intervals like in the example above). This means that it checks the condition periodically first after the given poll delay, and subsequently with the given poll interval; that is conditions are checked after pollDelay then pollDelay+pollInterval, then pollDelay + (2 * pollInterval), and so on. If you change the poll interval the poll delay will also change to match the specified poll interval unless you've specified a poll delay explicitly.

Fibonacci Poll Interval

The FibonacciPollInterval generates a non-linear poll interval based on the fibonacci sequence. Usage example:

with().pollInterval(fibonacci()).await().until(..);

where fibonacci is statically imported from org.awaitility.pollinterval.FibonacciPollInterval. This will generate a poll interval of 1, 1, 2, 3, 5, 8, 13, .. milliseconds. You can configure the time unit to use, for example seconds instead of milliseconds:

with().pollInterval(fibonacci(TimeUnit.SECONDS)).await().until(..);

Or a bit more english-like:

with().pollInterval(fibonacci().with().timeUnit(SECONDS).and().offset(5)).await().until(..);

Offset means that the fibonacci sequence is started from this offset (by default the offset is 0). The offset can also be negative (-1) to start with 0 (fib(0) = 0).

Iterative Poll Interval

A poll interval that is generated by a function and a start duration. The function is free to do anything with the duration. For example:

await().with().pollInterval(iterative(duration -> duration.multiply(2)), Duration.FIVE_HUNDRED_MILLISECONDS).until(..);

or a bit more english-like:

await().with().pollInterval(iterative(duration -> duration.multiply(2)).with().startDuration(FIVE_HUNDRED_MILLISECONDS)).until(..);

This generates a poll interval sequence that looks like this (ms): 500, 1000, 2000, 4000, 8000, 16000, ...

Note that if you specify a poll delay this delay will take place before the first poll interval is generated by this poll interval. See javadoc for more info.

Custom Poll Interval

It's possible to roll your own poll interval by implementing the PollInterval interface. This is a functional interface so in Java 8 you can just do like this:

await().with().pollInterval((__, previous) -> previous.multiply(2).plus(1)).until(..);

In this example we create a PollInterval that is implemented as a (bi-) function that takes the previous poll interval duration and multiplies it by 2 and adds 1. __ just signals that we don't care about the poll count that is also provided by the PollInterval interface. Poll count is required when creating poll intervals that are not (only) interested in the previous duration but rather generates its duration based on the number of times it has been called. For example the FibonacciPollInterval uses only the poll count:

await().with().pollInterval((pollCount, __) -> new Duration(fib(pollCount), MILLISECONDS)).until(..);

I most cases it shouldn't be necessary to implement a poll interval from scratch. Supply a function to the IterativePollInterval instead.

Condition Evaluation Listener

Awaitility 1.6.1 introduced the concept of Condition Evaluation Listener. This can be used to get information each time a condition has been evaluated by Awaitility. You can for example use this to get the intermediate values of a condition before it is fulfilled. It can also be used for logging purposes. For example:

with().
         conditionEvaluationListener(condition -> System.out.printf("%s (elapsed time %dms, remaining time %dms)\n", condition.getDescription(), condition.getElapsedTimeInMS(), condition.getRemainingTimeInMS())).
         await().atMost(Duration.TEN_SECONDS).until(new CountDown(5), is(equalTo(0)));

will print the following to the console:

    org.awaitility.AwaitilityJava8Test$CountDown expected (<0> or a value less than <0>) but was <5> (elapsed time 101ms, remaining time 1899ms)
    org.awaitility.AwaitilityJava8Test$CountDown expected (<0> or a value less than <0>) but was <4> (elapsed time 204ms, remaining time 1796ms)
    org.awaitility.AwaitilityJava8Test$CountDown expected (<0> or a value less than <0>) but was <3> (elapsed time 306ms, remaining time 1694ms)
    org.awaitility.AwaitilityJava8Test$CountDown expected (<0> or a value less than <0>) but was <2> (elapsed time 407ms, remaining time 1593ms)
    org.awaitility.AwaitilityJava8Test$CountDown expected (<0> or a value less than <0>) but was <1> (elapsed time 508ms, remaining time 1492ms)
    org.awaitility.AwaitilityJava8Test$CountDown reached its end value of (<0> or a value less than <0>) (elapsed time 610ms, remaining time 1390ms)

There's a built-in ConditionEvaluationListener for logging called ConditionEvaluationLogger that you can use like this:

with().conditionEvaluationListener(new ConditionEvaluationLogger()).await(). ...

Awaitility 4.0.2 introduces three new hooks (default methods) in the ConditionEvaluationListener interface:

Method Description
beforeEvaluation Hook that is called before a condition is evaluated
exceptionIgnored Handle ignored exception that get thrown while condition evaluation
onTimeout Hook that is called when a condition has timed out

As of Awaitility 4.1.0 ConditionEvaluationLogger takes a Consumer<String> with a pre-assembled logging message. You can use this to for example log intermediary values using slf4j instead of System.out.println or log to to disk or a data structure such as CopyOnWriteArrayList. For example:

await().conditionEvaluationListener(new ConditionEvaluationLogger(log::info)).until(<some condition>);

This will log using the logger log instead of printing to the console.

Fail-Fast Conditions

Awaitility (as of version 4.1.0) supports something called "fail-fast conditions". This is a special condition that, if fulfilled, will throw a org.awaitility.core.TerminalFailureException immediately, thus failing the test. This is good when you have a condition that you know beforehand should never be evaluated to true. So instead of waiting the full duration of a normal condition evaluation period, the test will fail-fast if this condition evaluates to true. For example:

await().timeout(Duration.ofSeconds(5))
       .failFast(() -> orders.findById(1234).state == PAID)
       .until(() -> orders.findById(1234).state == VOIDED);

This will fail-fast if the state of order 1234 is ever equal to "PAID" during the 5-second evaluation of the condition specified in "until".

If you want a more descriptive error message you can supply a "fail-fast reason":

.. .failFast("Order should never be considered paid when it's voided", () -> orders.findById(1234).state == PAID)

As of 4.2.1, you can also use assertions for fail-fast conditions:

await().timeout(Duration.ofSeconds(5))
       .failFast("Order should never be considered paid when it's voided", 
                 () -> assertThat(orders.findById(1234).state).isEqualTo(PAID))
       .until(() -> orders.findById(1234).state == VOIDED);

This will include both the message ("Order should never be considered paid when it's voided") and the AssertionError provided by assertj (or your assertion library of choice) for better error details.

It's also possible to configure a fail-fast reason globally for all tests:

Awaitility.setDefaultFailFastCondition(something::thatReturnsTrueOrFalse);
Awaitility.setDefaultFailFastCondition(() -> assertThat(something).isEqualTo("error"));

Important

Awaitility does nothing to ensure thread safety or thread synchronization! This is your responsibility! Make sure your code is correctly synchronized or that you are using thread-safe data structures such as volatile fields or classes such as AtomicInteger and ConcurrentHashMap.

Links and code examples

  1. Awaitility test case
  2. Awaitility test case Java 8
  3. Field supplier test case
  4. Atomic test case
  5. Presentation from Jayway's KHelg 2011