java mockito中文 - Mockito可以捕獲多次調用方法的參數嗎?




mockito教學 junit (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的第二次調用的參數?


Answers

由於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")));

如果你被綁定到較低的Java版本,那也不是那麼糟糕:

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

當然,這些都不能驗證呼叫的順序 - 您應該使用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")));

請看看mockito-java8項目,它可以進行如下調用:

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

如果你不想驗證所有對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());

我認為它應該是

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

來自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());

您也可以使用@Captor註釋的ArgumentCaptor。 例如:

@Mock
List<String> mockedList;

@Captor
ArgumentCaptor<String> argCaptor;

@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開始,您可以使用無參數的any方法,並且編譯器會推斷出類型參數:

verify(bar).doStuff(any());

說明

Java 8中的新事物是表達式的目標類型將被用來推斷其子表達式的類型參數。 在Java 8之前,只有用於類型參數推斷的方法的參數(大部分時間)。

在這種情況下, doStuff的參數類型將成為any()的目標類型,並且any()的返回值類型將被選擇為匹配該參數類型。

原始類型

不幸的是,這對原始類型不起作用:

public interface IBar {
    void doPrimitiveStuff(int i);
}

verify(bar).doPrimitiveStuff(any()); // Compiles but throws NullPointerException
verify(bar).doPrimitiveStuff(anyInt()); // This is what you have to do instead

問題是編譯器會推斷Integerany()的返回值。 Mockito不會意識到這一點(由於類型擦除)並返回參數類型的默認值,該值為null 。 運行時將嘗試通過調用intValue方法來解除返回值,並拋出異常。





java unit-testing mocking mockito