How does “static reflection” work in java? (ex. in mockito or easymock)
-
06-07-2019 - |
Question
I'm a .NET guy - and I mainly code in C#.
Since C# 3.0, we can leverage lambda expressions and expression trees to use static reflection. For example, it is possible to implement GetMethodName
in the following snippet to return the name of the method passed in parameter:
string methodName = GetMethodName( o => o.DoSomething());
Console.WriteLine(methodName); // displays "DoSomething"
Now, when I look at Mockito samples (or EasyMock ones) in the java world, I see:
LinkedList mockedList = mock(LinkedList.class);
when(mockedList.get(0)).thenReturn("first");
How does it work?
How does the when
method work ? How does it interpret mockedList.get(0)
as a call to the get method with 0 passed as parameter and not as a value?
Solution
Mocking libraries don't typically work with expression trees. They build a type which implements the appropriate interface and responds to method calls either by recording them or validating them and returning the preprogrammed responses. This is usually done with either a proxy (e.g. RealProxy in .NET, Proxy in Java) or with dynamic code generation.
In the case of EasyMock, it uses Proxy
(for interfaces, anyway), as you can see in the source code: look at org.easymock.internal.JavaProxyFactory
.
OTHER TIPS
Java mock libraries usually work like this:
When you create a mock, an actual proxy is created (be it from an interface or a sub-class), the instance is in "recording mode". This means that any subsequent call is recorded (method name, parameters, return expected). Notice that the proxy in recording mode does actually nothing but record the calls. There is no reflection per-se involved. No metadata discovery, etc. Of course these libraries do some tricks (such as storing invocations in a thread-local variable to handle methods that return void) but the idea remains the same.
Then, when the "replay mode" is started, the mock instance simply checks the expectations from the list of invocations (method+parameters & return values).
I’ve never worked with mockito or easymock but I don’t think the call does what you think it does. It does not interpret mockedList.get(0)
in any special way. The method get
is executed on the mockedList
object normally, and the result of that is handed in to when
.
mockedList.get(0)
is the syntax for a method call, and does exactly that. What that method does is not exactly clear. mockedList
's runtime type will be a subclass of LinkedList
returned by the mock
method, which can be implemented how ever the mocking framework sees fit.