Using AssertJ and Awaitility together thanks to Java 8 and lambdas
AssertJ and Awaitility are two of my favorites tools using in automatic code testing. Unfortunately until recently it was not possible to use it together. But then Java 8 entered the game and several dozens lines of code was enough to make it happen in Awaility 1.6.0.
AssertJ provides a rich set of assertions with very helpful error messages, all available though the fluent type aware API. Awaitility allows to express expectations of asynchronous calls in a concise and easy to read way leveraging an active wait pattern which shortens the duration of tests (no more sleep(5000)!).
The idea to use it together came into my mind a year ago when I was working on an algo trading project using Complex event processing (CEP) and I didn’t like to learn Hamcrest assertion just for asynchronous tests with Awaitility. I was able to do a working PoC, but it required to make some significant duplication in AssertJ (then FEST Assert) code and I shelved the idea. A month ago I was preparing my presentation about asynchronous code testing for the 4Developers conference and asked myself a question: How Java 8 could simplify the usage of Awaitility?
For the few examples I will use asynchronousMessageQueue
which can be used to send ping request and return number of received packets. One of the ways to test it with Awaitility in Java 7 (besides proxy based condition) is to create a Callable
class instance:
@Test public void shouldReceivePacketAfterWhileJava7Edition() { //when asynchronousMessageQueue.sendPing(); //then await().until(receivedPackageCount(), equalTo(1)); } private Callable<Integer> receivedPackageCount() { return new Callable<Integer>() { @Override public Integer call() throws Exception { return asynchronousMessageQueue.getNumberOfReceivedPackets(); } }; }
where equalTo()
is a standard Hamcrest matcher.
The first idea to reduce verbosity is to replace Callable
with a lambda expression and inline the private method:
@Test public void shouldReceivePacketAfterWhile() { //when asynchronousMessageQueue.sendPing(); //then await().until(() -> asynchronousMessageQueue.getNumberOfReceivedPackets(), equalTo(1)); }
Much better. Going forward lambda expression can be replaced with a method reference:
@Test public void shouldReceivePacketAfterWhile() { //when asynchronousMessageQueue.sendPing(); //then await().until(asynchronousMessageQueue::getNumberOfReceivedPackets, equalTo(1)); }
Someone could go even further and remove Hamcrest matcher:
@Test public void shouldReceivePacketAfterWhile() { //when asynchronousMessageQueue.sendPing(); //then await().until(() -> asynchronousMessageQueue.getNumberOfReceivedPackets() == 1); //poor error message }
but while it still works the error message becomes much less meaningful:
ConditionTimeoutException: Condition with lambda expression in AwaitilityAsynchronousShowCaseTest was not fulfilled within 2 seconds.
instead of very clear:
ConditionTimeoutException: Lambda expression in AwaitilityAsynchronousShowCaseTest that uses AbstractMessageQueueFacade: expected <1> but was <0> within 2 seconds.>
The solution would be to use AssertJ assertion inside lambda expression:
@Test public void shouldReceivePacketAfterWhileAssertJEdition() { //when asynchronousMessageQueue.sendPing(); //then await().until(() -> assertThat(asynchronousMessageQueue.getNumberOfReceivedPackets()).isEqualTo(1)); }
and thanks to the new AssertionCondition initially hacked within a few minutes it became a reality in Awaitility 1.6.0. Of course AssertJ fluent API and meaningful failure messages for different data types are preserved.
As a bonus all assertions that throw AssertionError (so particularly TestNG and JUnit standard assertions) can be used in the lambda expression as well (but I don’t know anyone who came back to “standard” assertion knowing the power of AssertJ).
The nice thing is that the changes itself leverage Runnable class to implement lambdas and AssertJ support and Awaitility 1.6.0 is still Java 5 compatible. Nevertheless for the sake of readability it is only sensible to use the new constructions in Java 8 based projects.
Btw, here are the “slides” from my presentation at 4Developers.
Reference: | Using AssertJ and Awaitility together thanks to Java 8 and lambdas from our JCG partner Marcin Zajaczkowski at the Solid Soft blog. |