【问题标题】:Is it possible using variables of ArgumentMatchers?是否可以使用 ArgumentMatchers 的变量?
【发布时间】:2021-12-30 07:49:59
【问题描述】:

假设我有一个这样的界面。

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

是否具有相同或等效的效果?

【问题讨论】:

    标签: mockito argument-matcher argument-matching


    【解决方案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);
    

    请参考How do Mockito matchers work?:

    实现细节

    匹配器(作为 Hamcrest 样式的对象匹配器)存储在一个堆栈中,该堆栈包含在一个名为 ArgumentMatcherStorage 的类中。 MockitoCore 和 Matchers 各自拥有一个 ThreadSafeMockingProgress 实例,该实例静态包含一个持有 MockingProgress 实例的 ThreadLocal。正是这个MockingProgressImpl 拥有一个具体的ArgumentMatcherStorageImpl。因此,mock 和 matcher 状态是静态的,但 Mockito 和 Matchers 类之间的线程范围一致。

    大多数匹配器调用仅添加到此堆栈中,and, or, and not 等匹配器除外。这完全对应(并依赖于)evaluation order of 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)) 作为其第二个参数的方法存根并清除堆栈。

    这展示了一些规则:

    • 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.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-10
      • 1970-01-01
      • 1970-01-01
      • 2017-10-30
      • 2016-06-13
      • 2011-06-03
      • 2022-01-15
      • 2023-03-26
      相关资源
      最近更新 更多