Unit testing asynchronous methods with Mockito.

Unit testing asynchronous methods with Mockito.

Testing asynchronous code is one of the biggest challenges we have as engineers. There are tools out there, which facilitate our job, so in this opportunity, let’s explore how a Framework like Mockito can help us to achieve this goal.

“Quality is never an accident; it is always the result of intelligent effort.”

Introduction

After promising that I would be writing and maintaining my blog, here I go again (3289423987 attempt). So in this occasion I want to write about Mockito… yes, this mocking framework that is a “must have” when writing unit tests in Java ;).

This is article assumes that you know what a Unit Test is and why you should write tests in general.

Also I strongly recommend this famous article from Martin Fowler which talks about Test Doubles, yes, it is a must read to understand this concept that we are going to use accross this article.

Common case scenario

Sometimes we have to test methods that use callbacks, meaning that they are asynchronous by definition.

These methods are not easy to test and using Thread.sleep(milliseconds) method to wait for the response is not a good practice and can convert your tests in non-deterministic ones (I have seen this many times in many code bases).

So how do we do this? Mockito to the rescue!

Let’s see an example

Suppose we have a class called DummyCaller that implements a DummyCallback and has a method doSomethingAsynchronously() that delegates its functionality to a collaborator of the the class called DummyCollaborator that has a doSomethingAsynchronously(DummyCallback callback) as well, but receives a callback as a parameter (in this case our DummyCallback), so this methods creates a new thread to run his job and then gives us a result when is done.

Here is the code to understand this scenario in a better way:

public interface DummyCallback {
  public void onSuccess(List<String> result);
  public void onFail(int code);
}
public class DummyCaller implements DummyCallback {
  private final DummyCollaborator dummyCollaborator;

  private List<String> result = new ArrayList<String>();

  public DummyCaller(DummyCollaborator dummyCollaborator) {
    this.dummyCollaborator = dummyCollaborator;
  }

  public void doSomethingAsynchronously() {
    dummyCollaborator.doSomethingAsynchronously(this);
  }

  public List<String> getResult() {
    return this.result;
  }

  @Override
  public void onSuccess(List<String> result) {
    this.result = result;
    System.out.println("On success");
  }

  @Override
  public void onFail(int code) {
    System.out.println("On Fail");
  }
}
public class DummyCollaborator {
  public static int ERROR_CODE = 1;

  public DummyCollaborator() {
    // empty
  }

  public void doSomethingAsynchronously (final DummyCallback callback) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          Thread.sleep(5000);
          callback.onSuccess(Collections.EMPTY_LIST);
        } catch (InterruptedException e) {
          callback.onFail(ERROR_CODE);
          e.printStackTrace();
        }
      }
    }).start();
  }
}

Creating our test class

We have 2 options to test our asynchronous method but first we will create our test class DummyCollaboratorCallerTest (for convention we just add Test at the end of the class so this becomes part of its name).

public class DummyCollaboratorCallerTest {
  // Class under test
  private DummyCaller dummyCaller;

  @Mock
  private DummyCollaborator mockDummyCollaborator;

  @Captor
  private ArgumentCaptor<DummyCallback> dummyCallbackArgumentCaptor;

  @Before
  public void setUp() {
    MockitoAnnotations.initMocks(this);
    dummyCaller = new DummyCaller(mockDummyCollaborator);
  }
}

So here we are using MockitoAnotations to initialize both Mock and ArgumentCaptor, but do not worry about them yet, cause this is what we will be seeing next.

The only thing to take into account here is that both mock and class under test are being initialized before each test is executed in the setUp() method (using the @Before annotation).

REMEMBER: For unit testing all the collaborators for a CUT (class under test) must be test doubles.

Let’s take a look at our 2 test solutions going forward.

Setting up an answer for our callback

This is our test case using a doAnswer() for stubbing a method with a generic Answer.

This means that since we need a callback to return immediately (synchronously), we generate an answer so when the method under test is called, the callback will be executed right away with the data we tell it to return.

Finally we call our real method and verify state and interaction.

@Test
  public void testDoSomethingAsynchronouslyUsingDoAnswer() {
    final List<String> results = Arrays.asList("One", "Two", "Three");
    // Let's do a synchronous answer for the callback
    doAnswer(new Answer() {
      @Override
      public Object answer(InvocationOnMock invocation) throws Throwable {
        ((DummyCallback)invocation.getArguments()[0]).onSuccess(results);
        return null;
      }
    }).when(mockDummyCollaborator).doSomethingAsynchronously(
        any(DummyCallback.class));

    // Let's call the method under test
    dummyCaller.doSomethingAsynchronously();

    // Verify state and interaction
    verify(mockDummyCollaborator, times(1)).doSomethingAsynchronously(
        any(DummyCallback.class));
    assertThat(dummyCaller.getResult(), is(equalTo(results)));
  }

Using an ArgumentCaptor

Our second option is to use an ArgumentCaptor.

Here we treat our callback asynchronously: we capture the DummyCallback object passed to our DummyCollaborator using an ArgumentCaptor.

Finally we can make all our assertions at the test method level and call onSuccess() when we want to verify state and interaction.

@Test
  public void testDoSomethingAsynchronouslyUsingArgumentCaptor() {
    // Let's call the method under test
    dummyCaller.doSomethingAsynchronously();

    final List<String> results = Arrays.asList("One", "Two", "Three");

    // Let's call the callback. ArgumentCaptor.capture() works like a matcher.
    verify(mockDummyCollaborator, times(1)).doSomethingAsynchronously(
        dummyCallbackArgumentCaptor.capture());

    // Some assertion about the state before the callback is called
    assertThat(dummyCaller.getResult().isEmpty(), is(true));

    // Once you're satisfied, trigger the reply on callbackCaptor.getValue().
    dummyCallbackArgumentCaptor.getValue().onSuccess(results);

    // Some assertion about the state after the callback is called
    assertThat(dummyCaller.getResult(), is(equalTo(results)));
  }

Conclusion

The main difference between both solutions is that when using DoAnswer() we are creating an anonymous inner class, and casting (in an unsafe way) the elements from invocation.getArguments()[n] to the data type we want, but in case we modify our parameters the test will “fail fast” letting know that something has happened.

On the other side, when using ArgumentCaptor we probably have more control cause we can call the callbacks in the order we want in case we need it.

In unit testing, this is a common case that sometimes we do not know how deal with, so in my experience using both solutions has helped me to have a robust approach when having to test asynchronous methods.

I hope you find this article useful, and as always, remember that any feedback is very welcome. Of course if you have any doubt do not hesitate to contact me.

Code Sample

Here is the link where you can find this example and others. Most of them are related to Java and Android because this comes from a presentation I gave a couple of months ago:


Further Reading

I highly recommend to take a look at Mockito documentation to have a better understanding of the framework. The documentation is very clear and has great examples.