Testing asynchronous code is a very broad topic, and we will not go into great details here. The fact is that the best way to test this kind of code is to make it synchronous and test it separately; this is crucial in terms of performance and execution time of unit tests. Imagine having quite a few such cases where you have to wait for a second or so for the test to complete. It would definitely increase the overall time of your tests and you wouldn't want that to happen.
You might, however, have a business requirement where it is crucial to verify whether some particular business feature was executed within the specified time (for example, a request has been sent within one second). In this recipe, we will take a closer look at what Mockito offers in this regard, and we'll do the same using the Awaitility
library.
We will test the PersonProcessor
class that performs some data processing and then delegates the saving of the person to a PersonSaver
class.
Let's imagine that PersonProcessor
is an endpoint that receives a Person
request. We'll assume that the business requirement is to save a person within one second from the time of receiving a request. We would like to write a test that will ensure that the savePerson(…)
method is executed within this specified time boundary.
All the logic done by PersonProcessor
is done in a separate thread. For simplicity, we will not use any ExecutorServices
; instead, we will start a thread manually. Also, we are not doing any real computations; instead, we are making the thread sleep for some time as shown in the following code:
public class PersonProcessor { private final PersonSaver personSaver; public PersonProcessor(PersonSaver personSaver) { this.personSaver = personSaver; } public void process(final Person person) { new Thread(new Runnable() { @Override public void run() { try { // simulating time consuming actions Thread.sleep(500); } catch (InterruptedException e) { System.err.printf("The thread got interrupted [%s]%n", e); } personSaver.savePerson(person); } }).start(); } }
To verify whether a method was executed within the given time, you have to call Mockito.verify(mock, Mockito.timeout(millis)).methodToVerify();
.
You can find a JUnit test of our system under test in the following code; see Chapter 1, Getting Started with Mockito, for the TestNG configuration:
@RunWith(MockitoJUnitRunner.class) public class PersonProcessorTest { @Mock PersonSaver personSaver; @InjectMocks PersonProcessor systemUnderTest; @Test public void should_process_person_within_specified_time() { // when systemUnderTest.process(new Person()); // then verify(personSaver, timeout(1000)).savePerson(any(Person.class)); } }
The timeout(…)
method instantiates the Timeout
object that implements the VerificationWithTimeout
interface. By default, when you call timeout(…)
, you set the expectation that the method will be executed only once.
What about the situations in which you would like to check whether the method got invoked, for example, at least twice? VerificationWithTimeout
gives you additional methods to do this: times(…)
, never()
, atLeastOnce()
, atLeast(…)
, and only()
. So, to check whether the method was executed twice, you would have to write the following code:
verify(personSaver, timeout(1000).atLeast(2)).savePerson(any(Person.class));
The preceding example is a very simple one, and Mockito doesn't offer you too much flexibility in terms of providing more advanced time conditions for the verification. There are libraries that are dedicated to this purpose. We'll have a look at the Awaitility
library (https://code.google.com/p/awaitility/). To put it briefly, Awaitility
is an open source library founded by JayWay, which gives you a Domain Specific Language (DSL) that allows you to define the expectations of an asynchronous system in a very elegant manner.
Before going further, we have to add Awaitility
to the classpath. To do this, let's use either Maven or Gradle (for manual installation, you can download the JAR files from https://code.google.com/p/awaitility/wiki/Downloads).
The following is the configuration for Gradle:
testCompile 'com.jayway.awaitility:awaitility:1.6.0'
The following is the configuration for Maven:
<dependency> <groupId>com.jayway.awaitility</groupId> <artifactId>awaitility</artifactId> <version>1.6.0</version> <scope>test</scope> </dependency>
Although there is no special integration of Awaitility
and Mockito, you can use it for the sake of verification. Let's try to rewrite our previous test using Awaitility
(we have statically imported the Awaitility.await()
method):
@RunWith(MockitoJUnitRunner.class) public class PersonProcessorTest { @Mock PersonSaver personSaver; @InjectMocks PersonProcessor systemUnderTest; @Test public void should_process_person_within_specified_time() { // when systemUnderTest.process(new Person()); // then await().atMost(1, SECONDS).until(personIsSaved()); } private Callable<Boolean> personIsSaved() { return new Callable<Boolean>() { @Override public Boolean call() throws Exception { try { verify(personSaver).savePerson(any(Person.class)); return true; } catch (AssertionError assertionError) { return false; } } }; } }
In this example, Awaitility
will execute the body of the instantiated Callable's call()
method each 100 ms (the default poll value) and wait for a positive result for, at most, one second. In Callable
, we have to catch the Mockito AssertionError
because Awaitility
will re-throw this exception and the test will fail. In that case, we just need to return false
so that Awaitility
knows that it should retry the method execution until it receives a positive result.
Awaitility
project's home page at https://code.google.com/p/awaitility/18.116.27.178