假设我有一个看起来像这样的界面。
interface Some {
/**
* Does some on specified array with specified index and returns the array.
*
* @param array the array.
* @param index the index.
* @returns given {@code array}.
*/
byte[] some(byte[] array, int index);
}
这里有一个简单的存根,使some
方法只返回给定的数组。
Some some = spy(Some.class);
when(some.some(any(), anyInt())
.thenAnswer(i -> i.getArguments(0)};
是否有可能或像这样修改上面的代码有任何意义?
Some some = spy(Some.class);
byte[] array = any(); // @@?
int index = anyInt(); // @@?
when(some.some(array, index) // @@?
.thenAnswer(i -> i.getArguments(0)};
它是否具有相同或同等的效果?
在发布的代码中,测试通过,效果等效。在一般情况下,情况并非如此,您很容易破解代码。
例如,这有效:
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 和匹配器类之间始终是线程范围的。
大多数匹配器调用仅添加到此堆栈中,但
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)
标记为最近的呼叫。- 调用
when(false)
,它丢弃其参数并准备存根方法quux(int, int)
中标识在 5 中。只有两个有效状态是堆栈长度为 0(相等)或 2(匹配器),并且堆栈上有两个匹配器(步骤 1 和 4),因此 Mockito 使用第一个参数的any()
匹配器存根方法,第二个参数使用and(gt(10), lt(20))
并清除堆栈。这演示了一些规则:
莫基托分不清
quux(anyInt(), 0)
和quux(0, anyInt())
的区别。它们看起来都像是堆栈上有一个 int 匹配器的quux(0, 0)
调用。因此,如果使用一个匹配器,则必须匹配所有参数。呼叫顺序不仅重要,而且是使这一切发挥作用的原因。将匹配器提取到变量通常不起作用,因为它通常会更改调用顺序。但是,将匹配器提取到方法效果很好。
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.