Чи може Mockito збирати аргументи методу, який називається кілька разів?


446

У мене є метод, який викликається двічі, і я хочу захопити аргумент виклику другого методу.

Ось що я спробував:

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Виняток, оскільки Мокіто вважає, що його doSomethingслід називати лише один раз.

Як я можу перевірити аргумент другого виклику doSomething?

Відповіді:


784

Я думаю, що так і має бути

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

3
Чи можете ви зафіксувати аргументи, передані doSomething()в кожному окремому виклику за допомогою цього?
мат б

36
Слід зазначити, що у випадку, якщо ви зробите щось подібне: Person person = new Person("John"); doSomething(person); person.setName("Jane"); doSomething(person);захоплений аргумент буде однаковим двічі (адже насправді це той самий об’єкт особи), тож capturedPeople.get(0).getName() == capturedPeople.get(1).getName() == "Jane"див. Також groups.google.com/forum/#!msg/mockito/ KBRocVedYT0 / 5HtARMl9r2wJ .
asmaier

2
Це приємно, але як я можу перевірити два виклики об'єктів, що відрізняються типом? Наприклад, ExecutorService.submit (новий MyRunableImpl ()); а потім ExecutorService.submit (новий MyAbodyRunableImpl ())?
Леон

Якщо потрібно обробити випадок , описаний @asmaier, я відправив відповідь тут: stackoverflow.com/a/36574817/1466267
SpaceTrucker

1
Для тих, хто все ще цікавиться відповіддю на запитання Леона, ви використовуєте загальний базовий клас ( Runnable) і, якщо потрібно, робіть більш конкретну перевірку типу захопленого аргументу.
Матвій

50

Оскільки 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")));

2
Це приємна техніка. На даний момент я отримую досить виразний результат: "Хотів, але не викликав: / n mockAppender.append (<менеджер індексів ut $$ lambda $ 5 9/1 3 1 9 5 1 0 1 6>);" - аргумент є CharSequence. Чи знаєте ви будь-яким способом змусити звіт належним чином роздрукувати "розшукуваний" аргумент?
мійський гризун

@mikerodent Криптовалютний результат можна виправити, якщо піти більш детальним маршрутом створення класу, який реалізує ArgumentMatcher <T>. Переосмислення методу toString у вашій реалізації надасть будь-яке повідомлення, яке ви хочете в тестовому висновку mockito.
Ной Соломон

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

чи є спосіб зафіксувати обидва значення?
Харс

9

Ви також можете використовувати @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));
}

6

З лямбдами Java 8 зручним способом є використання

org.mockito.invocation.InvocationOnMock

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

Я не в змозі побачити, як це зручніше, ніж старий спосіб. Я люблю добре вживати лямбда, але я не впевнений, чи це один.
Ерік Вілсон

0

Перш за все: ви завжди повинні імпортувати mockito static, таким чином код буде набагато більш читабельним (та інтуїтивно зрозумілим) - наведені нижче зразки коду вимагають його роботи:

import static org.mockito.Mockito.*;

У методі 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());
}

Список всіх переданих аргументів під час вашого тесту доступний за допомогою методу argument.getAllValues ​​().

Значення одного аргументу (останній виклик) доступне через argument.getValue () для подальшої маніпуляції / перевірки або будь-якого іншого ви хочете зробити.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.