In the code posted, the test passes and the effect is equivalent. This is not true in the general case, and you can easily break the code.
For example this works:
Some some = mock(Some.class);
byte[] any = any();
int index = anyInt();
when(some.some(any, index)).thenAnswer(i -> i.getArguments()[0]);
var res = some.some(new byte[]{1, 2}, 4);
While this with reordered variables does not work:
Some some = mock(Some.class);
int index = anyInt();
byte[] any = any();
when(some.some(any, index)).thenAnswer(i -> i.getArguments()[0]);
var res = some.some(new byte[]{1, 2}, 4);
Please refer to How do Mockito matchers work?:
Implementation details
Matchers are stored (as Hamcrest-style object matchers) in a stack contained in a class called ArgumentMatcherStorage. MockitoCore and Matchers each own a ThreadSafeMockingProgress instance, which statically contains a ThreadLocal holding MockingProgress instances. It's this MockingProgressImpl that holds a concrete ArgumentMatcherStorageImpl. Consequently, mock and matcher state is static but thread-scoped consistently between the Mockito and Matchers classes.
Most matcher calls only add to this stack, with an exception for matchers like and
, or
, and not
. This perfectly corresponds to (and relies on) the evaluation order of Java, which evaluates arguments left-to-right before invoking a method:
when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6] [5] [1] [4] [2] [3]
This will:
- Add
anyInt()
to the stack.
- Add
gt(10)
to the stack.
- Add
lt(20)
to the stack.
- Remove
gt(10)
and lt(20)
and add and(gt(10), lt(20))
.
- Call
foo.quux(0, 0)
, which (unless otherwise stubbed) returns the default value false
. Internally Mockito marks quux(int, int)
as the most recent call.
- Call
when(false)
, which discards its argument and prepares to stub method quux(int, int)
identified in 5. The only two valid states are with stack length 0 (equality) or 2 (matchers), and there are two matchers on the stack (steps 1 and 4), so Mockito stubs the method with an any()
matcher for its first argument and and(gt(10), lt(20))
for its second argument and clears the stack.
This demonstrates a few rules:
Mockito can't tell the difference between quux(anyInt(), 0)
and quux(0, anyInt())
. They both look like a call to quux(0, 0)
with one int matcher on the stack. Consequently, if you use one matcher, you have to match all arguments.
Call order isn't just important, it's what makes this all work. Extracting matchers to variables generally doesn't work, because it usually changes the call order. Extracting matchers to methods, however, works great.
int between10And20 = and(gt(10), lt(20));
/* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true);
// Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt().
public static int anyIntBetween10And20() { return and(gt(10), lt(20)); }
/* OK */ when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true);
// The helper method calls the matcher methods in the right order.