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

Eri*_*son 405 java unit-testing mocking mockito

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

这是我尝试过的:

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()
Run Code Online (Sandbox Code Playgroud)

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

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

pro*_*tif 724

我认为应该是

verify(mockBar, times(2)).doSomething(...)
Run Code Online (Sandbox Code Playgroud)

来自mockito javadoc的样本:

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());
Run Code Online (Sandbox Code Playgroud)

  • 应该注意的是,如果你做这样的事情:`人员=新人("约翰"); doSomething的(人); person.setName( "简"); doSomething(person);`捕获的参数将是相同的两次(因为实际上它是同一个person对象),所以`capturedPeople.get(0).getName()== capturedPeople.get(1).getName()= ="简",另请参阅https://groups.google.com/forum/#!msg/mockito/KBRocVedYT0/5HtARMl9r2wJ. (32认同)
  • 你能捕获在每个单独的调用中传递给`doSomething()`的参数吗? (2认同)
  • 这很好,但是如何测试两种不同类型的对象调用?例如ExecutorService.submit(new MyRunableImpl()); 然后是ExecutorService.submit(new MyAnotherRunableImpl())? (2认同)

Mac*_*ski 43

从Mockito 2.0开始,也有可能使用静态方法Matchers.argThat(ArgumentMatcher).在Java 8的帮助下,它现在更清晰,更易读:

verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("OneSurname")));
verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("AnotherSurname")));
Run Code Online (Sandbox Code Playgroud)

如果你与较低的Java版本联系在一起,那也不是那么糟糕:

verify(mockBar).doSth(argThat(new ArgumentMatcher<Employee>() {
        @Override
        public boolean matches(Object emp) {
            return ((Employee) emp).getSurname().equals("SomeSurname");
        }
    }));
Run Code Online (Sandbox Code Playgroud)

当然,没有一个可以验证调用的顺序 - 你应该使用InOrder:

InOrder inOrder = inOrder(mockBar);

inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("FirstSurname")));
inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("SecondSurname")));
Run Code Online (Sandbox Code Playgroud)

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

verify(mockBar).doSth(assertArg(arg -> assertThat(arg.getSurname()).isEqualTo("Surname")));
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的技术.我现在得到了一些相当神秘的输出:"通缉但没有被调用:/ n mockAppender.append(<指数管理员ut $$ lambda $ 5 9/1 3 1 9 5 1 0 1 6>);" - arg有一个`CharSequence`.你知道如何让报告正确地打印出"通缉"的arg吗? (2认同)

lre*_*der 25

如果你不想验证所有的呼叫doSomething(),只有最后一个,你可以使用ArgumentCaptor.getValue().根据Mockito javadoc:

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

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

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());
Run Code Online (Sandbox Code Playgroud)

  • @Harshit使用`List &lt;Foo&gt; foos = fooCaptor.getValues();` (2认同)

Mic*_*mal 8

您还可以使用@Captor带注释的ArgumentCaptor.例如:

@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));
}
Run Code Online (Sandbox Code Playgroud)


小智 6

使用Java 8的lambda,一种便捷的方法是使用

org.mockito.invocation.InvocationOnMock

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

  • 我不明白这比旧方法更方便。我喜欢很好地使用 lambda,但我不确定这是否是其中之一。 (3认同)

fl0*_*l0w 5

首先:您应该始终导入mockito static,这样代码将更具可读性(也更直观) - 下面的代码示例需要它才能工作:

import static org.mockito.Mockito.*;
Run Code Online (Sandbox Code Playgroud)

在 verify() 方法中,您可以传递 ArgumentCaptor 以确保测试中的执行,并传递 ArgumentCaptor 以评估参数:

ArgumentCaptor<MyExampleClass> argument = ArgumentCaptor.forClass(MyExampleClass.class);
verify(yourmock, atleast(2)).myMethod(argument.capture());

List<MyExampleClass> passedArguments = argument.getAllValues();

for (MyExampleClass data : passedArguments){
    //assertSometing ...
    System.out.println(data.getFoo());
}
Run Code Online (Sandbox Code Playgroud)

测试期间所有传递的参数的列表可通过 argument.getAllValues() 方法访问。

单个(最后调用的)参数的值可以通过 argument.getValue() 访问,以进行进一步的操作/检查或任何您想要做的事情。