Как проверить несколько вызовов методов с разными параметрами


116

У меня есть следующий метод, на котором я хочу проверить поведение.

public void methodToTest(Exception e, ActionErrors errors) {
    ...

    errors.add("exception.message", 
            ActionMessageFactory.createErrorMessage(e.toString()));

    errors.add("exception.detail",
            ActionMessageFactory.createErrorMessage(e.getStackTrace()[0].toString()));

    ...
}

В моем классе @Test я надеялся сделать что-то подобное, чтобы проверить, что это errors.add()вызывается с помощью «exception.message» и снова с «exception.detail»

verify(errors).add(eq("exception.message"), any(ActionError.class));
verify(errors).add(eq("exception.detail"), any(ActionError.class));

однако Мокито жалуется на следующее

Argument(s) are different! Wanted:
actionErrors.add(
    "exception.message",
    <any>
);

Actual invocation has different arguments:
actionErrors.add(
    "exception.detail",
    org.apache.struts.action.ActionError@38063806
);

Как я могу сказать Mockito, чтобы он проверял оба значения?


1
когда у вас есть 2 метода с разной подписью, вы можете написать отдельный тестовый пример для обоих.
Навин Бабу

8
Да, но в данном случае это та же сигнатура метода, но просто другие значения аргументов
Брэд,

можно попробовать использоватьMockito.reset()
takacsot

Ответы:


103

Дальнейшее чтение побудило меня попробовать использовать ArgumentCaptors и следующие работы, хотя и гораздо более подробные, чем мне хотелось бы.

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);

verify(errors, atLeastOnce()).add(argument.capture(), any(ActionMessage.class));

List<String> values = argument.getAllValues();

assertTrue(values.contains("exception.message"));
assertTrue(values.contains("exception.detail"));

есть ли способ убедиться, что определенные параметры были объединены в пару с использованием этого подхода? Скажем, например , метод Ора имел два аргумента и хотел убедиться , что они были названы вместе
committedandroider

1
Тестовый пример OP вызывает methodToTest()ровно один раз, поэтому этот ответ действительно подтверждает, что два вызова выполняются вместе. Захваченный List<String> values, который утверждается, будет содержать только два тестируемых значения и никаких других. Вы также можете добавить assertTrue(values.size == 2). Если это то, что вы хотите, я бы заменил 3 утверждения assertTrue одним Hamcrest ...assertThat(values, contains("exception.message", "exception.detail"));
Брэд

Разве тестовый пример OP не вызывает methodToTest () дважды?
Committedandroider

извините, я не понял. Я имел в виду сценарий, в котором OP хотел проверить, что два аргумента вызываются вместе. Таким образом, сигнатура метода будет выглядеть примерно так: public void methodToTest (Exception e, Message m, ActionErrors errors) {так что конкретное исключение вызывается с определенным сообщением. Я полагал , что вы могли бы просто иметь два ArgumentCaptors , а затем получить индекс и сравнить с использованием значений на этих индексов в обоих списках значений
committedandroider

Тестовый пример OP вызывает methodToTest()один раз. Это аргумент метода, ActionErrors errorsкоторый внутренне вызывается дважды.
Брэд

61

Если порядок обоих add()вызовов актуален, вы можете использовать InOrder:

InOrder inOrder = inOrder(errors);
inOrder.verify(errors).add(eq("exception.message"), any(ActionError.class));
inOrder.verify(errors).add(eq("exception.detail"), any(ActionError.class));

7
Достаточно передать один errorsаргумент: InOrder inOrder = inOrder(errors);(см. Документацию )
GreenhouseVeg

2
Что делать, если порядок НЕ актуален? что часто бывает.
haelix

1
@haelix В этом случае используйте ответ Брэдса. Преобразовать Listв Setи утверждают , что набор входов равно множество , задаваемое аргументом захватами.
flopshot

25

Попробуйте что-то вроде этого:

verify(errors, times(2))
     .add(AdditionalMatchers.or(eq("exception.message"), eq("exception.detail")),
          any(ActionError.class));

6
Ваш чек явно слишком расслаблен.
haelix

17

у вас вероятно есть проблема в вашем коде. Потому что на самом деле вы пишете этот код:

Map<Character, String> map = mock(Map.class);

map.put('a', "a");
map.put('b', "b");
map.put('c', "c");

verify(map).put(eq('c'), anyString());
verify(map).put(eq('a'), anyString());
verify(map).put(eq('b'), anyString());

Обратите внимание, что первая проверка не выполняется даже в отношении фактических вызовов.

Кроме того, я бы порекомендовал вам на самом деле не имитировать типы, которыми вы не владеете, например, тип struts.

[ИЗМЕНИТЬ @Brad]

После запуска кода Брайса (см. Выше) в моей среде IDE я вижу, что я использовал ActionError вместо ActionMessage, поэтому мой verify () не соответствовал. Сообщение об ошибке, которое я изначально опубликовал, вводило меня в заблуждение, заставляя думать, что это был первый несоответствующий аргумент. Оказывается, это был второй аргумент.

Итак, ответ на мой вопрос

/** 
 * note that ActionMessageFactory.createErrorMessage() returns ActionMessage
 * and ActionError extends ActionMessage
 */
verify(errors).add(eq("exception.message"), any(ActionMessage.class));
verify(errors).add(eq("exception.detail"), any(ActionMessage.class));

1
Не понимайте того, что пытаетесь сказать. Имеет ли значение порядок проверки? если порядок проверки имеет значение. Почему тогда здесь предоставляется API InOrder?
Александр Папченко

Как и то, что написано выше, порядок проверки не имеет значения; вот почему есть InOrder.
Brice

12

Вы можете использовать, Mockito.atLeastOnce()который позволяет Mockito пройти тест, даже если этот mockObject будет вызываться много раз.

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(1));

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(2));

1

1) Сообщите Mokito ожидаемое общее количество звонков.

2) Сообщите Mokito, сколько раз каждая комбинация параметров ожидалась.

verify(errors, times(2)).add(any(), any(ActionMessage.class));

verify(errors, atLeastOnce()).add(eq("exception.message"), any());
verify(errors, atLeastOnce()).add(eq("exception.detail"), any());

0

Аналогично @ sendon1928 мы можем использовать:

Mockito.times(wantedInvocationCount)

чтобы метод вызывал точное количество раз (на мой взгляд, предпочтительное решение). После мы можем позвонить

Mockito.verifyNoMoreInteractions(mock)

Чтобы убедиться, что этот макет больше не использовался ни в каком контексте. Полный пример:

Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(1));

Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(2));

Mockito.verifyNoMoreInteractions(mockObject)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.