Say What? Develop own Mockito Library?

Dave Parth
Globant
Published in
7 min readNov 6, 2019

Can we create our own Mockito Library? Or You want to learn behind the scene of the Mockito library?

Well, it’s a bit opinionated question as someone would come to you and say why you want to reinvent the wheel? and that’s true. But how can we get a deep understanding of how Mockito or any other mocking framework works internally? What goes under the hood? because developing and understanding things are quite different( thanks to StackOverflow ;) ).

Mockito
whenever(mockObject.methodYouWantedToMock())
.thenReturn(resultYouWanted)

Suppose I say to you it’s easy? will it motivate you to learn under the hood of mocking framework? Then Let’s get started….

@copyrights with giphy

There are two types of mock frameworks or we can say there are two types of mocking strategy.

  1. Proxy Based frameworks
  2. Bytecode manipulation frameworks

The easiest framework is the Proxy-based framework because to transmute byte-code you need to have a better understanding of byte-code itself.

Difference between both frameworks:

  1. Proxy-based frameworks are much easier to implement but have limitations of themselves.
  2. Bytecode manipulation is like becoming Avengers, I’m not kidding, it’s true. As it requires someone with byte code understanding like the screen shown in hacking movies. while the Proxy-based framework is much simpler to understand.

For now, in this story, we will stick to the Proxy-based framework.
Let’s understand Proxy first:
A proxy is just an object which will be used instead of the original object itself. If a method has been called then the proxy object will decide:
1. Handle call itself.
2. Delegate to the original class.

Note: Method passing to original class is called spying as we are logging or spying on the data it gets/returns from original method.
There’s also another terminology outside test framework which is called class delegation.

Limitation of a Proxy

There are a few important restrictions on the proxies. It’s not possible to:

  • intercept static method calls
  • intercept private method calls
  • intercept final method calls
  • build a proxy for a final class

To overcome limitations we need to do bytecode manipulations and that’s where PowerMockito comes into the picture as it uses a bytecode manipulation strategy to overcome the limitation of proxy.

@copyrights with giphy

Okay now, I’m stopping all theory and let’s move on to the code

Create a Proxy? How?

A proxy class has been provided with java reflection API.

Proxy.newProxyInstance(MockProxy.class.getClassLoader(), new Class[]{clazz}, invocationHandler);//Method parameters
//public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

`newProxyInstance` method takes a class loader, interfaces and invocation handler. Let’s go step by step:

  1. `Classloader`: It is referring to proxy class. Our custom proxy class which takes care of all handlings like mocking and storing references of the mocked methods and return values.
  2. Interfaces: It takes class reference which interface we will mock.
  3. Invocationhandler: It is used for the invocation of a method, so when we want to mock some method we usually set mock(obj.method()).thenReturn(returning value) so the class which invoked by proxy class when we write this signature is invocationHandler. To mark the method and we need to send this return value by getting data from invoke method.

Note: newProxyInstance only takes interfaces, if you want to mock the classes I’ll post another article continuing this basics about how to mock class.

Create a MockProxy class

class MockProxy {

companion object {
private var lastMockInvocationHandler: MockInvocationHandler? = null

/**
* Creates a mock based on a class.
*
*
@param clazz the class of the mock
*
@param <T> the type of the mock
*
@return the mock object
</T> */
fun <T> mock(clazz: Class<T>): T {
val invocationHandler = MockInvocationHandler()
return Proxy.newProxyInstance(
MockProxy::class.java.classLoader,
arrayOf<Class<*>>(clazz),
invocationHandler
) as T
}

/**
* This class is just needed to ERHALTEN the type information. So that we
* have the type information for the When::thenReturn method.
*
*
@param obj the value which we doesn't need.
*
@param <T> the type of the return value
*
@return an instance of When with the correct type information
</T> */
fun <T> `when`(obj: T?): When {
return When()
}
}

class When{

/**
* Sets the return value for the last method call.
*
*
@param retObj the return value
*/
fun thenReturn(retObj: Any) {
lastMockInvocationHandler!!.setRetObj(retObj)
}
}
}

So inside MockProxyClass, we have created a static method mock (or I could say companion method in Kotlin language).

There are two types of signatures we need to do in mock
1. Mock interface
2. Mock methods

To mock interface, we have a mock method in the MockProxy class. which takes a class as a parameter and gives a mocked proxy object using `newProxyInstance` method we looked earlier.
Now that we have a mocked object we can mock the method.

You will be thinking can’t we go and mock methods directly?
No, we can’t.
Why?
Because as we checked earlier the mock proxy has his limitations, Proxy can’t mock for static methods.

To mock the method we have created `when` method just like Mockito.

Okay, now we need to understand the logic behind how invocations happen so that next code of when method and other invocation classes can be explained.

So first let’s look at a mock method for mocking Interface.

fun <T> mock(clazz: Class<T>): T {
val invocationHandler = MockInvocationHandler()
return Proxy.newProxyInstance(
MockProxy::class.java.classLoader,
arrayOf<Class<*>>(clazz),
invocationHandler
) as T
}

This creates an invocationHandler and passes it to proxy class to make proxy.

MockProxy.`when`(fooInterfaceMock.foo()).thenReturn("Foo Fighters!")

Note that whenever we call mockobject.method(), every time the MockInvocationHandler will get invoked.
Why?
Because we have created a proxy interface it’s like a blank class which do not have any capability to do anything and so everything will be on our hand what to do.
and so just for readability we are using syntax MockProxy.`when`(fooInterfaceMock.foo())
If we look at the signature of when method it just returns the When() class does nothing with the parameter, we are just invoking the handler before then return method to get hold to which method and argument invoked and need to return which data.

When we are going to mock the method this invocation handler object will get invoked and we need to take all things like method, method arguments, and the return object so that when we try to call we know what needs to be returned.

Now as we can see clearly that the method is invoked by us after `when()`, so we need to hold all the references. we have created a class.

inner class DataHolder(
val method: Method?,
val args: Array<Any>?,
val retObj: Any?
)

A data holder class that will take method, method arguments, and the return object.

private class MockInvocationHandler : InvocationHandler {

private var lastMethod: Method? = null
private var lastArgs: Array<Any>? = null
private val dataHolders = ArrayList<DataHolder>()

/**
* Intercepts the method call and decides what value will be returned.
*/
@Throws(Throwable::class)
override fun invoke(proxy: Any?, method: Method?, args: Array<Any>?): Any? {
lastMockInvocationHandler = this
lastMethod = method
lastArgs = args

// checks if the method was already called with the given arguments
for (dataHolder in dataHolders) {
if (dataHolder.method == method && Arrays.deepEquals(dataHolder.args, args)) {
// if so than return the stored value
return dataHolder.retObj
}
}

// otherwise return null
return null
}

/**
* Adds the return value for the last called method with the last given arguments.
*
*
@param retObj the return value
*/
fun setRetObj(retObj: Any?) {
dataHolders.add(DataHolder(lastMethod, lastArgs, retObj))
}
}

Here, MockInvocationHandler implements InvocationHandler, why?
Because of the `Proxy.newProxyInstance` takes `InvocationHandler` interface as a parameter and we need a logic to store mocked methods and arguments.
The method `invoke` will be called whenever we try to use the proxy object’s method. and we are storing values of mocked method and arguments into `lastMethodInvocation` and lastMethodArguments varaiables.
Again confused? why do we need to store it in `lastVariable`? because until now we don’t have a return object, so there’s no need to add data to an array if we are not having return object.

And when we invoke thenReturn it will invoke `setRetObj` method from `lastInvocationhandler`, where we are just adding all details to the array list.

Note: We were not checking if we already mocked that method, so if you run above code then it will not update new return data. To update that below is the code:

/**
* Adds the return value for the last called method with the last given arguments.
*
*
@param retObj the return value
*/
fun setRetObj(retObj: Any?) {
var isFound = false
for (dataHolder in dataHolders) {
if (dataHolder.method == lastMethod && Arrays.deepEquals(
dataHolder.args,
lastArgs
)
) {
dataHolder.retObj = retObj
isFound = true
}
}
if (!isFound)
dataHolders.add(DataHolder(lastMethod, lastArgs, retObj))
}

Now we added all details and the mock data classes into the array we can start testing.

Create an interface and JUnit test file

public interface FooInterface {

public String foo();

public String echo(String val);

}
-------------------------------------------
object MainTest {

@JvmStatic
fun main(args: Array<String>) {

// java.lang.reflect.Proxy
println("# MockProxy")
val fooInterfaceMock = MockProxy.mock(FooInterface::class.java)
MockProxy.`when`(fooInterfaceMock.foo()).thenReturn("Foo Fighters!")
MockProxy.`when`(fooInterfaceMock.foo()).thenReturn("Foo Fighters again changed!")
println(fooInterfaceMock.foo())
MockProxy.`when`(fooInterfaceMock.echo("echo")).thenReturn("echo")
println(fooInterfaceMock.echo("echo"))
MockProxy.`when`(fooInterfaceMock.echo("hello")).thenReturn("world")
println(fooInterfaceMock.echo("hello"))
println()
}
}

and the output will be:

# MockProxy
Foo Fighters again changed!
echo
world

And we conclude our testing custom Mockito library.

@copyrights with giphy

TL’DR

There are a lot of things that you can try with and play with using this type of custom proxy class. We have checked How proxy generated?, How it works?, How invocation handler will do it’s work like a dark magic spell and created a small proxy for method invocation and return our own data.

My Gist Link of this custom Mockito, find on this link. -> link

--

--

Dave Parth
Globant
Writer for

Developer, Photographer. Flutter and Automation enthusiast.