在贴出的代码中,测试通过,效果是等价的。在一般情况下并非如此,您可以轻松破坏代码。
例如这有效:
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);
虽然这对重新排序的变量不起作用:
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);
请参阅Mockito 匹配器如何工作?:
实施细节
匹配器(作为 Hamcrest 样式的对象匹配器)存储在名为ArgumentMatcherStorage的类中的堆栈中。MockitoCore 和 Matchers 各自拥有一个ThreadSafeMockingProgress实例,该实例静态包含一个持有 MockingProgress 实例的 ThreadLocal。正是这个MockingProgressImpl拥有一个具体的ArgumentMatcherStorageImpl。因此,模拟和匹配器状态是静态的,但在 Mockito 和 Matchers 类之间是一致的线程范围。
大多数匹配器调用仅添加到此堆栈中,除了and
,or
和not
. 这完全对应(并依赖于)Java 的评估顺序,它在调用方法之前从左到右评估参数:
when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6] [5] [1] [4] [2] [3]
这将:
- 添加
anyInt()
到堆栈。
- 添加
gt(10)
到堆栈。
- 添加
lt(20)
到堆栈。
- 删除
gt(10)
和lt(20)
添加and(gt(10), lt(20))
。
- 调用
foo.quux(0, 0)
,它(除非另外存根)返回默认值false
。在内部 Mockito 标记quux(int, int)
为最近的调用。
- Call
when(false)
,它丢弃它的参数并准备quux(int, int)
5 中标识的存根方法。仅有的两个有效状态是堆栈长度为 0(相等)或 2(匹配器),并且堆栈上有两个匹配器(步骤 1 和 4),所以Mockito 使用any()
匹配器为其第一个参数和and(gt(10), lt(20))
第二个参数存根该方法并清除堆栈。
这展示了一些规则:
Mockito 无法区分quux(anyInt(), 0)
和quux(0, anyInt())
。它们看起来都像是quux(0, 0)
在堆栈上调用一个 int 匹配器。因此,如果您使用一个匹配器,则必须匹配所有参数。
调用顺序不仅重要,它也是使这一切正常工作的原因。将匹配器提取到变量通常不起作用,因为它通常会更改调用顺序。然而,将匹配器提取到方法中效果很好。
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.