How does a Mockito spy know when it is spying?

551 views Asked by At

This code from the documentation is totally baffling me:

List list = new LinkedList();
List spy = spy(list);

when(spy.size()).thenReturn(100); // <--- how does this spy know 
// not to call the real method????

//using the spy calls *real* methods
spy.add("one");
spy.add("two");

I get it, Mockito is weird and hardly still in Java. Confusing thing is spy.* has to evaluate fully before it knows whether it's wrapped in a when() or something. How on earth would the first spy.* method not call on the real object but the later ones doe?

2

There are 2 answers

4
Sotirios Delimanolis On

I don't know the exact implementation, but I can take a guess.

The call to spy(...) first proxies the given object and keeps it as a reference to delegate calls.

The call

when(spy.size()).thenReturn(100);

is practically equivalent to

Integer result = spy.size();
OngoingStubbing<Integer> stubbing = when(result); // result is useless
stubbing.thenReturn(100);

The first call to size() is invoked on the proxy. Internally, it can register the call, pushing it, for example, on a static (global) Mockito stack. When you then invoke when(), Mockito pops from the stack, recognizes the call to size() as needing stubbing and performs whatever logic required to do so.

This can explain why stubbing in a multithreaded environment is bad business.

4
Flo On

According to the documentation the first when(spy.size()).thenReturn(100) will actually invoke the real List.size() method, see: http://mockito.github.io/mockito/docs/current/org/mockito/Mockito.html#13

Each subsequent call of course will then return the mocked result.

If you don't want the real method to be called (e.g. when(spy.get(0)).thenReturn(...) would probably throw an IndexOutOfBoundsException, you have to use this pattern: doReturn(...).when(spy).get(0);