Mockito 模拟调用两次的方法调用

Mockito Mock a method call called twice

我正在尝试使用 mockito 来模拟一个方法。但是 class 我注入的模拟调用方法两次,同时发送相同类型的两个不同对象,但取决于对象中的值确定方法的输出。

所以,例如,如果我想模拟

public ArrayList<example> attemptToMock(testObject testing)

让我们输入 testObject 中有一个字符串值。

所以如果 testObject 中的字符串值是 "OK" 那么 attemptToMock 应该输出一个包含两个对象的数组。如果testObject字符串值为"NO"则发出的Array列表只有一个Object.

如何编写测试来处理调用,以便 class 可以在同一方法中调用 attemptToMock 两次,并且我可以根据值模拟其输出在 testObject 内。我可以模拟它来发送不同的数组。

您可以访问传递给模拟方法调用的参数,并通过使用 Answer 接口相应地改变 return 值。查看 this question, and the docs for Answer.

的答案

基本上,这里发生的唯一 weird/non-obvious 事情是您必须将参数向下转换为您期望的类型。因此,在您的情况下,如果您正在模拟采用单个 'TestObject' 参数的方法,那么您必须在 'answer' 实现中执行类似的操作:

Object[] args = invocation.getArguments();
TestObject testObj = (TestObject) args[0];
if ("OK".equals(testObj.value)) {
  return new ArrayList(value1, value2);
} else if ("NO".equals(testObj.value)) {
  return new ArrayList(singleObject);
}

几个选项:

  • Override equals and hashCode 在您的对象 (TestObject) 上。这仅在对象上的所有值都是可预测的情况下才可行,并且可能比其他解决方案更有效,但是如果您无论如何都需要编写 equalshashCode(对于 Map 和 Set 行为,对于例如)这是一个合理的解决方案。

    // Mockito compares with objects' equals(Object) methods by default.
    when(collaborator.attemptToMock(object1)).thenReturn(array1);
    when(collaborator.attemptToMock(object2)).thenReturn(array2);
    
  • 写一个Hamcrest matcher并用它来匹配数组。对于特定情况,这可以作为 equals 的紧凑模拟,如果您需要在许多测试中基于相同的值更改行为,则特别方便。

    public class IsATestObjectWithValue extends TypeSafeMatcher<TestObject> {
      private final String expectedValue;
    
      public IsATestObjectWithValue(String expectedValue) {
        super(TestObject.class);
        this.expectedValue = expectedValue;
      }
    
      @Override public void matchesSafely(TestObject object) {
        // object will never be null, but object.value might.
        return expectedValue.equals(object.value);
      }
    }
    

    现在你可以写一个等价的匹配如上:

    when(collaborator.attemptToMock(argThat(new IsATestObjectWithValue("OK")))
        .thenReturn(array1);
    when(collaborator.attemptToMock(argThat(new IsATestObjectWithValue("NO")))
        .thenReturn(array2);
    
  • 使用答案,。匿名内部答案很常见而且非常简洁,您可以访问所有论点。这对于一次性解决方案特别有用,或者如果您希望在传入无效值 (testObject.value) 时明确并立即使测试失败。

  • 作为最后的手段,如果调用的顺序是可预测的,您可以 return 多个值按顺序排列。

    when(collaborator.attemptToMock(any(TestObject.class)))
        .thenReturn(array1).thenReturn(array2);
    when(collaborator.attemptToMock(any(TestObject.class)))
        .thenReturn(array1, array2);  // equivalent
    

    以上任何一行都将 return array1 用于第一次调用和 array2 用于第二次调用和之后的所有调用,无论参数如何。这个解决方案将 比你原来的问题要求的更脆弱 ——如果呼叫顺序改变,或者如果其中一个呼叫被编辑或重复,它会失败——但是如果测试非常临时或顺序绝对固定,有时是最紧凑的解决方案。