2

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?

djechlin
  • 59,258
  • 35
  • 162
  • 290
  • It has been answered before. Please see http://stackoverflow.com/questions/11620103/mockito-trying-to-spy-on-method-is-calling-the-original-method – nadirsaghar Nov 13 '14 at 23:17
  • @nadirsaghar I don't see how the answer you linked to answers this question. – JB Nizet Nov 13 '14 at 23:31

2 Answers2

2

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);

Flo
  • 1,469
  • 1
  • 18
  • 27
  • This is true. And I am an advocate of always using `doReturn(...).when(...)` instead of worrying about when you need `doReturn(...).when(...)` and when it's OK to use `when(...).thenReturn(...)`. In fact, I have posted Mockito answers on this site in which I recommend not using `when(...).thenReturn(...)` at all. – Dawood ibn Kareem Nov 13 '14 at 23:43
  • @DavidWallace do you mean that you recommend to always use doReturn() when using spies, or to always use it, even with mocks? I tend to always use it with spies, but find the classical wen(...).then... more readable and thus use that for mocks. – JB Nizet Nov 14 '14 at 00:14
  • 1
    My preference is to use it even with mocks. There is no point learning two syntaxes when just one will cover every case. And it's difficult to remember all the cases when `when/thenReturn` won't work. @JBNizet – Dawood ibn Kareem Nov 14 '14 at 00:15
  • 1
    @JBNizet - Have a look at my answer on http://stackoverflow.com/q/11462697/ . It looks like I incorrectly state that `when` can't be used on spies. This might have been true at the time that I wrote it; I'm not sure. So I will go back and correct this answer. In any case, the basic message of my answer is still true. – Dawood ibn Kareem Nov 14 '14 at 00:19
1

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.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 2
    @DavidWallace You're going to make me look at the source, aren't you? – Sotirios Delimanolis Nov 13 '14 at 23:27
  • Well, I was thinking about going and looking at the source myself, just to remind myself. But you're actually very close. There isn't a global stack, and the stubbing call is registered in the spy itself. But all the other details are right. I think I downvoted you too hastily, and I'll remove my downvote now. – Dawood ibn Kareem Nov 13 '14 at 23:32
  • Hmm, had to make an insignificant edit to remove my downvote. I hate this site sometimes. – Dawood ibn Kareem Nov 13 '14 at 23:33
  • ... and the thing created by `spy` doesn't use delegation. It's in a class that adds logic for stubbing and verification, but for all its methods, it calls the parent method on itself, not on some delegate object. – Dawood ibn Kareem Nov 13 '14 at 23:38