Verifying the method invocation within the specified time

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.

Getting ready

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(); 
    }

}

How to do it…

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));
  }
    
}

How it works...

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));

There's more...

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.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.27.178