(Sorry this got long. TL;DR: Mockito records the method call behind the scenes.)
C cm = mock(C.class);
At this point, you may think that cm
is an instance of C
...and you would be wrong. Instead, cm
is an instance of a proxy object Mockito
writes that implements C
(and thus can be assigned to fields/variables of type C) but records everything you ask for and behaves the way you stub it to.
Let's write a mock class manually...and give it one more method, let's say int add(int a, int b)
, which adds a
and b
in the actual class.
class MockC extends C {
int returnValue;
@Override int add(int a, int b) {
return returnValue;
}
}
There! Now whenever you call add
, it won't add the two numbers, but instead just return the single return value. Makes sense. But what if you want to verify the calls later?
class MockC extends C {
List<Object[]> parameterValues = new ArrayList<>();
int returnValue;
@Override int add(int a, int b) {
parameterValues.add(new Object[] { a, b });
return returnValue;
}
}
So now you can check the parameterValues
list and make sure it was called as expected.
Here's the thing: Mockito generates a proxy using CGLIB that acts like MockC automatically, keeping all of the interactions and return values in one big static list. The list is called RegisteredInvocations
, and instead of an Object[]
every method call for every mock is an Invocation
, but the idea is the same.
To understand a little more about RegisteredInvocations
and why the removeLast
method it exposes is so important, read the code in InvocationContainer
. Because Mockito records every call, it will naturally record the interaction contained within when
. As soon as Mockito sees when
, it removes the last recorded interaction (InvocationContainerImpl.java line 45) and uses it as the template for your stubbing--reading the argument values from the Invocation object itself.
That takes care of most of it, except for argument matchers like eq
and any
: Turns out that those are just kept on a fancy stack called ArgumentMatcherStorage
. The when
call checks how many matchers are on the stack: For the add
example, zero matchers tells Mockito to infer equality with every recorded argument, and two matchers tells Mockito to pop those off the stack and use those. Just one matcher means Mockito can't tell which integer you're trying to match and throws the often-confusing InvalidUseOfMatchersException
, which is why when using matchers you need to match every argument if you match any at all.
Hope that helps!
EDIT: This answer describes how the when
method works in more technical detail.