How Mockito Works?

Gokhan Gorali
5 min readApr 26, 2020

--

In this post I will try to explain the inner workings of the popular Java mocking framework Mockito.

How Mockito.when() works

I’ve been using Mockito for quite a while, but something was not ‘clicking’ when typing the when(…).thenReturn(…) construct. Here is what I mean:

Let’s define a class named Target, our mocking target.

public class Target {

public String doIt(String thingName) {

return String.format("Done: %s", thingName);
}

}

When we want to change the behaviour of the doIt method we first get a mock instance with Mockito.mock and then define the expected behaviour using the Mockito.when method. For example, if we want it to return “Mocked!” instead of “Done: Read” if the method’s argument is “Read”, we do it like this:

Target mockTarget = Mockito.mock(Target.class);
Mockito.when(mockTarget.doIt("Read")).thenReturn("Mocked!");

So that, we get the desired mocking behaviour:

mockTarget.doIt("Read").equals("Mocked!"); // true

From time to time I was making the following mistake and getting a compilation error immediately:

// Does not compile
Mockito.when(mockTarget).doIt("Read")

After all, “when” method may know about methods of the mocked instance, right? If we look closer, the correct version below looks more like a mistake because we apply the when method to a return value of a method of the class we are mocking.

// Correct 
Mockito.when(mockTarget.doIt("Read"))...

We apply the when method to the result of mockTarget.doIt(“Read”).

This is a bit curious. In this case the value is a String, so we can write a funny statement like this:

Mockito.when("Hey, I am a String too").thenReturn("Mocked!");

The line above compiles but fails in runtime.

org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.

The error message is a bit strange. Because it kind of implies that the when method can understand whether its argument comes from a method call or not. Very intelligent method huh?

In fact, as you’d expect it is required that the method call on the mock happens before the when statement. The following block does the mocking as expected.

mockTarget.doIt("Read");
Mockito.when("I am a String too!").thenReturn("Mocked!");
mockTarget.doIt("Read").equalsIgnoreCase("Mocked!"); // true!

Why does this work? Short answer: Because the doIt method is intercepted and does stuff other than its original intention.

Long answer: mockTarget is a “poisoned” instance. When we call a method on it the method call is intercepted to save the method invocation details to some mocking context. When we invoke the when() method we are in fact recalling the last registered method call from that context andthenReturn() saves the return value for it.

The recorded behaviour is played back when our test invokes the mocked method again which triggers the interceptor to recall the return value we provided.

In Mockito source code, we see when method does not use the parameter methodCall. But it tries to get the OngoingStubbing from an instance of MockingProgress. This OngoingStubbing is what I call as mocking context above.

public <T> OngoingStubbing<T> when(T methodCall) {
MockingProgress mockingProgress = mockingProgress();
mockingProgress.stubbingStarted();
@SuppressWarnings("unchecked")
OngoingStubbing<T> stubbing = (OngoingStubbing<T>) mockingProgress.pullOngoingStubbing();
if (stubbing == null) {
mockingProgress.reset();
throw missingMethodInvocation();
}
return stubbing;
}

To provide its fluent and easy to use API, Mockito creates this beautiful illusion with its when method. It won’t be so much popular if someone blindly “refactored” when and renamed it togetOngoingStubbing. :)

The funny detail/illusion above was the reason I wanted to write this post (COVID-19 lockdown also helped). So you might just stop reading now. Or, if you bear with me we’ll try to create a miniature Mockito in the following section.

OK, How Mockito works?

Simply put, Mockito works by storing and retrieving method invocation details on mocks using method interception.

Method interception is a technique used in Aspect Oriented Programming(AOP) to address cross-cutting concerns in a software system like logging, statistics etc., by intercepting method calls. I find this short description from Google Guice helpful if you are unfamiliar.

Mockito does not use an AOP framework like Spring AOP or AspectJ to achieve method interception. It uses the runtime code generation and manipulation library Byte Buddy for interception and the hassle-free reflection library Objenesis for initializing mock objects.

Now, we’ll create a proof of concept mocking library by partly creating simplified versions of corresponding classes from Mockito source code; we’ll ‘mock’ Mockito so to say: Let’s call it Mocky :)

Another nice thing about Mockito is, it does not need initialization. You can start using it right away through its API consisting of static methods. We’ll have the same static API:

The static ‘ease’ is for our users so we delegate the work to a class called RealMocky:

RealMocky keeps a list of InvocationDetails, which is the structure we keep the method invocation details in. The when method just returns the last InvocationDetails in the list. This good old ArrayList will serve as Mockito’s OngoingStubbing for our purposes.

InvocationDetails keeps the attached class’ name, the method name and an Object[] of arguments. The method’s return value is kept in a variable called result which is set by the thenReturn(T t)method. InvocationDetails also implements equals and hashcode methods.

Now the crucial mocking bit: We have a ByteBuddyMockCreator class to do that. With ByteBuddy’s fluent API, in a few lines we are opening up the class at runtime and putting some stuff in it.

A lot of things are happening here:

  • We subclass the class
  • We say we want to intercept any method in the class
  • We delegate interception to the InterceptionDelegate class, a delegator that passes the context and invokes the MockyInterceptor.
  • We define a new private field with type MockyInterceptor named interceptor.
  • We make the class implement our MockyInterceptable interface which has a method to set the interceptor field.
  • We load the class with the ClassLoader.
  • We instantiate the class with reflection using Objenesis. (Objenesis makes us not worry about constructors)
  • We set the MockyInterceptor field which has a reference to the InvocationDetails list.

By this process above, the mocked Target object returned by mock(Target.class)will have all of its methods intercepted by an interceptor called MockyInterceptor.

So when we invoke mockTarget.doIt(“Read”) this ends up in the invoke method of the interceptor. If it has not been run with the given arguments before, interceptor adds the InvocationDetails to the list of recorded InvocationDetails and returns a default value for the doIt method. If it was run with the same arguments before, it finds the InvocationDetails in the list and returns the previously saved return value with the when, thenReturn construct.

So we have all the bits and pieces for our proof of concept mocking library. Let’s summarize how it functions:

So there we have it. A proof of concept mocking library. I hope I could explain the inner workings of Mockito and the illusion of “when”. The source code is here. You will find some classes I omitted in the post and also a mock creation method using Spring AOP in the repository.

Thank you for reading!

--

--

Gokhan Gorali

Develops software at Elsevier — opinions are my own.