Freitag, 20. Juni 2014

Explanation how proxy based Mock Frameworks work

Yes or you
are now interested? Great, then you should read this article. Otherwise you are
probably lost and I can't help you. Sorry.

The first
important thing to know is that there are two types of frameworks.

the proxy based mock frameworks:
Mockito, EasyMock, jMock, ...

the mock frameworks based on bytecode
manipulation: PowerMock, ...

There is a
big different between those both concepts.

are much easier to implement but
they are more restricted in the features they can support.

bytecode manipulation should tell
you everything you should know about it: it's based on "very dark magic". It can
break on major Java releases. So be careful if you start to use such
frameworks, because they could prevent you from upgrade your Java version.
PowerMock for example builds on top of Javassist. A framework which makes
bytecode manipulation more simple.

In this
article I will explain only how the proxy based mock frameworks like Mockito
works. Because it's quite easy to understand how this kind of mock frameworks
work. The knowledge will probably help you to use those frameworks if you know
how they work and where the limits are. So you will never try to do anything
which is technically impossible.

What is a Proxy?

A proxy is
just an object which will be used instead of the original object. If a method
of the proxy object is called then the proxy object can decide what it will do
with this call:

delegate
it to the original object

handles
the call itself

Proxies can
be used to implement some kind of permission system. The proxy checks if the
user is allowed to call the method and if the user doesn't have the permission
then it throws an exception.

A proxy
doesn't require an instance of an interface/class if the proxy handles all
method invocations itself. Mockito.mock(Foo.class) is now easily explained.
This code just creates a proxy object for the Foo class.

Limits of a Proxy

There are a
few important restrictions to 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

If you want to understand these limitations then read my other blog entry about: Explanation how CGLIP proxies workAnother
restriction is that you have always to create the proxy explicitly. So it's not
possible to say, that all Foo instances, created with new Foo()
should be automatically wrapped into a proxy object. With PowerMock such things
are possible.

How to create a Proxy

If you look
into the Java API you will find the java.lang.reflect.Proxy class.

/**

*
Returns an instance of a proxy class for the specified interfaces

* that
dispatches method invocations to the specified invocation

*
handler.

*

*
@paramloader the class loader to
define the proxy class

*
@paraminterfaces the list of
interfaces for the proxy class to implement

It's quite
simple to use. To showcase the usage of this proxy we just build our own simple
mock framework - you find the complete source code at the end of the article. We create the static mock method which returns the proxy
object.

So each
call to a method of the proxy object will return null. At this stage we have an proxy
object on which we can call methods.

How to dynamically define
the Proxy?

Now the
question is how to setup the proxy to return something else than just null. We
need to do something like: Mock.when(foo.echo("foo")).thenReturn("foo")

But how
does this work? To understand how this work you have to analyze the one line of code
very carefully and think about what it actually does.

What does the when()
method actually gets?

In the case
of our example it would just gets a null object. In the
first step we created the foo object which is a proxy which always return null.
The when() method doesn't get the method echo() with a parameter. It's an common
method call. It's a call to the proxy which will return null and pass the null value to the when() method. That's it.

How the thenReturn()
method works?

You have
learned that the when() method just got null. So how can the thenReturn() call
work? Think a few minutes about it. It's nothing special. It can be called at
most "a little trick".

The
solution is simple: with static variables in which the state is stored.

in
the MockInvocationHandler we store the method and the arguments of the last call

in
the Mock class - Mock.when(foo.echo("foo")).thenReturn("foo") - we store the reference to the MockInvocationHandler which was
called last

These two
steps happens in the invoke() method of the proxy object - in the MockInvocationHandler. The when() method doesn't
have any logic. When thenReturn() is called then we store the return value for
the stored (remembered) MockInvocationHandler with it's last method and arguments. If the
proxy is called again then it will return the stored return value (if it's the
same method gets called with the same parameter).

Basically that's it

I hope you
could follow so far. I will summarize it again with other words

create
a proxy

if
a proxy method is called then remember which method was called. This proxy
method call is normally located inside of the Mock.when() method - even if it has no relationship to the when() method.

if
thenReturn(value) is called store the "value" to the stored/remembered method.

the
proxy returns the "value" if the method is called again with the
correct arguments.

The genius behind this is

The very
simple API which makes the whole thing looks like very nice. Another great decision is that
the when() method uses generics so that the thenReturn() method is type safe.

But what's about classes?

The current
solution only works for interfaces. Because java.lang.reflect.Proxy only
supports interfaces and not classes. So we need another mechanism to create the proxy. We have to dig a little be deeper and finally come to CGLIB - Code
Generation Library. So we are back to magic. But it's by far not so dangerous
like bytecode manipulation. We just use bytecode generation to create the proxy which shouldn't fail.
In fact many tools are using CGLIB (e.g. Spring, Hibernate).

To create a proxy with CGLIB isn't any more difficult than with the Java`s Proxy class.

You can create a proxy for a real object. All calls will be delegated to the real
object expect if the method call is redefined. With CGLIB this is very easy to
build. If you want to know more just take a look at the source code.

Source Code

A simple mock framework to demonstrate how the proxy based mock frameworks work. With two implementations: