Mocking with Mockito: advanced techniques

Marcos Abel
Trabe
Published in
3 min readOct 28, 2019
Photo by Louis Reed on Unsplash

Mockito is a mocking library for Java. It provides a simple API that allows you to tailor the behavior of your mocks to your specific needs. Mockito is a powerful tool and provides support for almost any testing scenario you can think about. In this article, we will take a look at some advanced stuff you can do with this library.

With great power comes great responsibility

Using mockito you can implement really complex interactions. But most of the time you don’t want to use all that power. I have seen overcomplicated tests plenty of times. I have implemented overcomplicated tests that don’t provide any advantage over simple behavior tests. Sometimes we feel that we need to take full advantage of our tools and go for the complicated option in scenarios where the easy, simple one is enough.

The techniques I cover in the following sections are useful in very specific scenarios but shouldn’t be used for every test in your system.

Argument captors

Sometimes we want to assert stuff about the arguments passed to a mocked method call. Mockito provides ArgumentCaptor<T> to help us with this task.

Let’s write an interface BarService for our examples:

The Cocktail class will look as follows:

And CocktailFamily will be something like this:

Let’s write an implementation for our interface:

As we can see here, our service has two collaborators, CocktailAccessor and StatsAccessor:

We want to test that when we call getTopThreeCocktails, our code calls to the CocktailAccessor and StatsAccessor with the right parameters:

As you can see, we don’t assert anything about the result itself, we are just checking the implementation details.

Test caches

A classic example where we want to test our implementation details is that of methods using caches. We want to verify that the underlying services are called only when the value is not present in the cache.

In a real-world example, we would use some real cache implementation but, for the sake of simplicity, we will use a hand-made cache implementation here.

Let’s add a trivial cache implementation to our service:

And modify our constructor to instantiate a cache for us:

With this code in place, we can update our BarService to include a new method getAlcoholicCocktails:

The implementation for this new method will use our cache:

Now we can test our cache behavior:

Of course, this is an oversimplified scenario, but hopefully good enough to illustrate the process.

Accessing method parameters

Sometimes you need to use your mocked method’s parameters to compute the response. For those scenarios, you can use doAnswer.

Your mock can correctly respond to any call to statsAccessor.getTopRatedCocktailIds (assuming that the List passed as a parameter is long enough to handle all your testing cases).

This technique is useful in a bunch of situations and can be used to mock complex iterations.

One specific thing you can achieve with doAnswer is the ability to throw exceptions or return mocked values according to your specific logic.

Mocks returning mocks

Usually, the calls to our mocks return real POJOs but we can make our mocks return mocked objects instead. This technique allows us to perform verification on the objects returned by our mocked call.

If you execute this example with the default Mockito configuration, you are going to bump into an error:

Mockito cannot mock/spy because : - final class

This is because we are trying to mock immutable objects and Mockito doesn’t support that feature with the default configuration. Luckily for us, we can avoid this problem configuring a mockito plugin.

Returning mocks as the result of a mocked method is an extremely powerful tool. You get the ability to assert things about how your implementations interact with the objects returned by your mocks. But It’s very easy to implement extremely complex tests using this technique.

Wrapping up

In this article, we took a look at some advanced techniques that allow you to mock complex interactions between the code you are testing and your mocks.

These techniques are a valuable tool for some scenarios. But when you mock complex interactions your tests are usually more coupled with the fine details of your implementation and become more fragile.

Use them only when you can’t achieve your goals using the regular stuff.

You have been warned!

--

--