是否可以使用 ArgumentMatchers 的变量?



假设我有一个看起来像这样的界面。

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 和匹配器类之间始终是线程范围的。

大多数匹配器调用仅添加到此堆栈中,但andornot等匹配器除外。这完全符合(并依赖于)Java的求值顺序,它在调用方法之前从左到右计算参数:

when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6]      [5]  [1]       [4] [2]     [3]

这将:

  1. anyInt()添加到堆栈。
  2. gt(10)添加到堆栈。
  3. lt(20)添加到堆栈。
  4. 删除gt(10)lt(20)并添加and(gt(10), lt(20))
  5. 调用foo.quux(0, 0),它(除非另有存根)返回默认值false。在内部,Mockito将quux(int, int)标记为最近的呼叫。
  6. 调用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.

相关内容

  • 没有找到相关文章

最新更新