Awaitility – Java DSL for easy testing of asynchronous systems

Introduction

Testing asynchronous systems is hard. Not only does it require handling threads, timeouts and concurrency issues, but the intent of the test code can be obscured by all these details. Awaility is a DSL that allows you to express expectations of a asynchronous system in a concise and easy to read manner.

Simple example

So let’s assume that we send an “add user” message to our asynchronous system like this:

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:

Awaitility will now block the test execution and wait until the new user is added to the database. 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.

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

Better reuse

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:

The userRepositorySize method is now a Callable of type Integer:

equalTo is a standard Hamcrest matcher specifiying 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:

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

Reduce verboseness

Since Java is so verbose Awaitility also provides a way achieve the same result by building up the supplier part without defining a Callable:

to is method in Awaitility which you can use to define the suppling part inline in the await statement. Which option you like best depends on the use case and readability.

More features

Awaitility uses polling to check if a condition is fulfilled. You can easily specify a polling interval and poll delay (the delay before the first poll commits):

In some cases it’s also useful to create named awaits for example if you need multiple awaits in one test. A naive example:

So if the first await statement fails the TimeoutException will indicate that it was the “user 1” statement that failed and so on.

Conclusion

We hope that you find Awaitility easy to use when you need to synchronize asynchronous behaviour in Java. However be aware that since it uses polling to verify that the condition is fulfilled it’s not recommended to use it for precise performance testing. In these cases it could be better to use an AOP framework such as AspectJ and leverage on its compile-time weaving.

Please visit our homepage for more info and downloads.

This Post Has 14 Comments

  1. Very cool lib wich help me a lot to make cleaner asynch assertions.

    The only thing i would like to see is more clear message about assertions failures instead of something like (java.util.concurrent.TimeoutException: Condition didn’t complete within 2 seconds.), especially the hamcrest mathing failure message

  2. You can alias your awaits like this:

    await(“something”).until(somethingHappens());

    Then the timeout exception will indicate something like:
    java.util.concurrent.TimeoutException: Condition “something” didn’t complete within 2 seconds.

    But perhaps it would be possible to create a better message by default. I’ll add it as an issue.

    Thanks for you comments!

  3. Awaitility 1.1 has just been released and includes better default error messages on timeout. Hope you like it!

  4. Hi Johan, tried your new release, better but still not really that ! here is our real world scenario where we are using an hamcrest matcher:

    waitAtMost(TWO_SECONDS).until(rawPrice(currencyPair, BookSideType.ask), is(askPrice));

    When it fails we get with your new release the following message wich is better than before :
    Condition returned by method “rawPrice” in class foo.bar.waiters.conditions.PriceConditions was not within 5 seconds.

    what we would really like to see in the team is what was the last hamcrest assertion error message, it would really help to troubleshoot problems faster.

    In junit if you do a simple assertion with hamcrest like :
    assertThat(1, is(2));

    you will get a clear error message looking like that: java.lang.AssertionError:
    Expected: is
    got:

    So we could have something like : Condition returned by method “rawPrice” in class foo.bar.eforex.waiters.conditions.PriceConditions was not within 5 seconds : Expected: is got:

    Modifying AbstractHamcrestCondition and catching the error thrown at line 35 should do the job, but these are my 2 cents :)

    Thanks a lot

    C.Munger

  5. Hi again. I’ve sent a mail to johan with a patched version of the trunk with such functionality ! :) hope you will like it let me know if you receive it.

    Regards c.Munger

  6. We’ve just released version 1.2 which includes the improved error message.

  7. Nifty! I use it to test our MVC based application containing asyncronous callbacks that has to be done before continuing the test thread. Awaitility helps me with this.

  8. We could also write
    await().atMost(5, SECONDS).until(newUserWasAdded());

    as
    Thread.sleep(5000);
    if(newUserWasAdded(){
    // execute logic
    }
    whats the difference in 2?

Leave a Reply

Close Menu