Mockito的参数捕获行为

6

我遇到了一个问题,需要跟踪某个方法的调用,但只针对特定的参数进行跟踪。请参见以下问题:

@Test
public void simpleTest() {
    ArgumentCaptor<Pear> captor = ArgumentCaptor.forClass(Pear.class);
    Action action = mock(Action.class);
    action.perform(new Pear());
    action.perform(new Apple());
    verify(action, times(1)).perform(captor.capture());
}

static class Action {

    public void perform(IFruit fruit) {}

}
static class Pear implements IFruit {}
static class Apple implements IFruit {}

interface IFruit {}

但是获得:
org.mockito.exceptions.verification.TooManyActualInvocations: 
action.perform(<Capturing argument>);
Wanted 1 time:
But was 2 times. Undesired invocation:
..

我做错了什么?Mockito版本1.10

说实话,这只是一个例子,我的代码更加复杂,而且我不知道perform方法会被调用多少次,使用Apple类。这对我来说不重要。我只需要验证针对Pear类的perform方法是否被调用。

更新: 我需要验证使用Pear类的方法是否被调用一次。比方说,Action是Transaction,perform是save(DomainObject)。我需要确保save(MyDomainObject)被调用了一次,但不管之前保存了多少个对象。这个操作对于测试来说是原子操作,我不能在这些操作之间重置模拟对象。


5
你调用了两次,并试图验证你只调用了一次? - khelwood
@Berger 在他的情况下,应该使用 times(2) 以通过测试。 - amseager
但是我只想验证带有参数类型Pear.class的方法调用。你有什么想法吗? - Bogdan Samondros
尝试使用 verify(action, times(1)). perform(argumentThat(yourImplementation)) 来检查参数。 - Alexander.Furer
3个回答

4

要验证使用Pear实例参数的调用次数,您可以使用:

verify(action, times(1)).perform(isA(Pear.class));

注意,自Mockito 2.1版本以来,以下方法也可以使用:

Cf. Mockito. 验证方法参数是否为特定类

verify(action, times(1)).perform(any(Pear.class));

参见 public static T any(Class type)

...该方法是isA(Class)的别名...

...自Mockito 2.1.0版本以来,any()和anyObject()不再是此方法的别名。


只是为了澄清一下:any()实际上匹配任何东西。那里的类并没有以任何方式限制它,它只是为了与Java的类型检查兼容。 - Florian Schaetz
1
@FlorianSchaetz:刚刚查了一下,似乎自Mockito 2.1以来,any(Class)isA(Class)的别名,而不再是any()/anyObject()的别名,请参见:https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/ArgumentMatchers.html#any-java.lang.Class- - Arnaud

3
使用自定义的 Capture 类来解决问题。
  @Test
  public void simpleTest() {
    MyArgumentCaptor pearCaptor = new MyArgumentCaptor(Pear.class);
    Action action = mock(Action.class);

    action.perform(new Pear());
    action.perform(new Apple());

    verify(action, times(1)).perform((IFruit) argThat(pearCaptor));

    System.out.println(pearCaptor.getMatchedObj());
  }


  class MyArgumentCaptor extends InstanceOf {
    private Object matchedObj;

    MyArgumentCaptor(Class<?> clazz) {
      super(clazz);
    }

    @Override
    public boolean matches(Object actual) {
      boolean matches = super.matches(actual);
      if (matches) {
        matchedObj = actual;
      }
      return matches;
    }

    Object getMatchedObj() {
      return matchedObj;
    }
  }

您的这两行代码节省了我的时间。非常感谢…… verify(action,times(1))。perform((IFruit)argThat(pearCaptor)); System.out.println(pearCaptor.getMatchedObj()); - chamzz.dot

0
你正在调用两次操作,但只期望一次调用(times(1))。如果它被调用了两次,请尝试使用times(2),或者如果您不关心调用次数,请省略它。
action.perform(new Pear());
action.perform(new Apple());

说实话,这只是一个例子,我的代码更复杂,我不知道 perform 方法会被调用多少次,传入的参数是 Apple.class。但这对我来说并不重要,我只需要验证 perform(Pear.class) 方法是否被调用。 - Bogdan Samondros
如果您不关心该方法被调用的次数,请更新描述。 - nilsmagnus
我需要验证是否调用了Pear.class中的方法一次。假设Action是Transaction,perform是save(DomainObject)。因此,我需要确保save(MyDomainObject)被调用了一次,但不管之前保存了多少个对象都可以。这个操作对于测试来说是原子性的,我不能在这些操作之间重置模拟。 - Bogdan Samondros

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接