0

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

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)};

它有相同或等效的效果吗?

4

1 回答 1

1

在贴出的代码中,测试通过,效果是等价的。在一般情况下并非如此,您可以轻松破坏代码。

例如这有效:

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,ornot. 这完全对应(并依赖于)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. 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.
于 2021-12-30T09:14:55.763 回答