关于java:Mockito可以捕获多次调用方法的参数吗?

Can Mockito capture arguments of a method called multiple times?

我有一个方法被调用两次,我想捕获第二个方法调用的参数。

以下是我的尝试:

1
2
3
4
5
ArgumentCaptor<Foo> firstFooCaptor = ArgumentCaptor.forClass(Foo.class);
ArgumentCaptor<Foo> secondFooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar).doSomething(firstFooCaptor.capture());
verify(mockBar).doSomething(secondFooCaptor.capture());
// then do some assertions on secondFooCaptor.getValue()

但我得到了一个TooManyActualInvocations例外,因为mockito认为doSomething只应该调用一次。

如何验证doSomething第二次调用的参数?


我想应该是

1
verify(mockBar, times(2)).doSomething(...)

Mockito Javadoc的样品:

1
2
3
4
5
6
ArgumentCaptor<Person> peopleCaptor = ArgumentCaptor.forClass(Person.class);
verify(mock, times(2)).doSomething(peopleCaptor.capture());

List<Person> capturedPeople = peopleCaptor.getAllValues();
assertEquals("John", capturedPeople.get(0).getName());
assertEquals("Jane", capturedPeople.get(1).getName());


由于mockito 2.0,还可以使用静态方法matchers.arghat(argumentmatcher)。在Java 8的帮助下,它现在变得更干净,更易于阅读:

1
2
verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("OneSurname")));
verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("AnotherSurname")));

如果你被绑定到较低的Java版本,也没有那么糟糕:

1
2
3
4
5
6
verify(mockBar).doSth(argThat(new ArgumentMatcher<Employee>() {
        @Override
        public boolean matches(Object emp) {
            return ((Employee) emp).getSurname().equals("SomeSurname");
        }
    }));

当然,这些都不能验证通话顺序—您应该使用inorder:

1
2
3
4
InOrder inOrder = inOrder(mockBar);

inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("FirstSurname")));
inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("SecondSurname")));

请看一下mockito-java8项目,它可以进行如下调用:

1
verify(mockBar).doSth(assertArg(arg -> assertThat(arg.getSurname()).isEqualTo("Surname")));


如果您不想验证所有对doSomething()的调用,只验证最后一个调用,则可以使用ArgumentCaptor.getValue()。根据Mockito Javadoc:

如果多次调用该方法,则返回最新捕获的值

所以这是可行的(假设Foo有一个方法getName()):

1
2
3
4
ArgumentCaptor<Foo> fooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar, times(2)).doSomething(fooCaptor.capture());
//getValue() contains value set in second call to doSomething()
assertEquals("2nd one", fooCaptor.getValue().getName());


也可以使用@captor注释的argumentcaptor。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Mock
List<String> mockedList;

@Captor
ArgumentCaptor<String> argCaptor;

@BeforeTest
public void init() {
    //Initialize objects annotated with @Mock, @Captor and @Spy.
    MockitoAnnotations.initMocks(this);
}

@Test
public void shouldCallAddMethodTwice() {
    mockedList.add("one");
    mockedList.add("two");
    Mockito.verify(mockedList, times(2)).add(argCaptor.capture());

    assertEquals("one", argCaptor.getAllValues().get(0));
    assertEquals("two", argCaptor.getAllValues().get(1));
}

用Java 8的lambdas,一个方便的方法就是使用

1
2
3
4
5
6
org.mockito.invocation.InvocationOnMock

when(client.deleteByQuery(anyString(), anyString())).then(invocationOnMock -> {
    assertEquals("myCollection", invocationOnMock.getArgument(0));
    assertThat(invocationOnMock.getArgument(1), Matchers.startsWith("id:"));
}