Mockito Макет вызова метода, вызываемого дважды
Я пытаюсь использовать mockito, чтобы издеваться над методом. Однако класс, который я внедряю, mocks с вызовами метода дважды при отправке в двух разных объектах одного типа, но в зависимости от значений в объекте определяют выход метода.
Так, например, если я пытаюсь издеваться
public ArrayList<example> attemptToMock(testObject testing)
Давайте посидеть типа testObject
имеет строковое значение в нем.
Так что, если строковое значение в testObject равно "OK", тогда attemptToMock
должен вывести массив из двух объектов в нем. Если testObject
Строковое значение равно "NO", тогда отправленный список массивов имеет только один объект.
Как написать тест для обработки вызова, чтобы класс мог вызывать attemptToMock
дважды, в рамках одного и того же метода, и я могу смоделировать его вывод так, в зависимости от значений в testObject
, Я могу высмеять его, чтобы отправить разные массивы.
2 ответа
Несколько вариантов:
Override
equals
а такжеhashCode
на вашем объекте (TestObject). Это возможно только в том случае, если все значения вашего объекта предсказуемы и могут потребовать больше усилий, чем другие решения, но если вам нужно написатьequals
а такжеhashCode
в любом случае (например, для поведения Map и Set) это разумное решение.// Mockito compares with objects' equals(Object) methods by default. when(collaborator.attemptToMock(object1)).thenReturn(array1); when(collaborator.attemptToMock(object2)).thenReturn(array2);
Напишите совпадение Hamcrest и используйте его для сопоставления массивов. Это действует как компактный аналог
equals
для конкретного случая, и это особенно удобно, если вам нужно изменить поведение на основе одного и того же значения во многих тестах.public class IsATestObjectWithValue extends TypeSafeMatcher<TestObject> { private final String expectedValue; public IsATestObjectWithValue(String expectedValue) { super(TestObject.class); this.expectedValue = expectedValue; } @Override public void matchesSafely(TestObject object) { // object will never be null, but object.value might. return expectedValue.equals(object.value); } }
Теперь вы можете написать эквивалентное совпадение, как указано выше:
when(collaborator.attemptToMock(argThat(new IsATestObjectWithValue("OK"))) .thenReturn(array1); when(collaborator.attemptToMock(argThat(new IsATestObjectWithValue("NO"))) .thenReturn(array2);
Используйте ответ, как описано в wdf. Анонимные внутренние ответы являются общими и довольно лаконичными, и вы получаете доступ ко всем аргументам. Это особенно хорошо для одноразовых решений, или если вы хотите явно и сразу же провалить тест, если передано недопустимое значение (testObject.value).
В крайнем случае, если порядок вызовов предсказуем, вы можете возвращать несколько значений последовательно.
when(collaborator.attemptToMock(any(TestObject.class))) .thenReturn(array1).thenReturn(array2); when(collaborator.attemptToMock(any(TestObject.class))) .thenReturn(array1, array2); // equivalent
Любая из приведенных выше строк вернется
array1
за первый звонок иarray2
для второго вызова и всех вызовов после него, независимо от параметра. Это решение будет гораздо более хрупким, чем требует ваш первоначальный вопрос - оно потерпит неудачу, если изменится порядок вызовов, или если один из вызовов будет отредактирован или повторен, - но иногда является наиболее компактным решением, если тест очень временно или если заказ абсолютно фиксирован.
Вы можете получить доступ к параметрам, переданным в вызов имитируемого метода, и соответствующим образом изменить возвращаемое значение, используя интерфейс ответа. Смотрите ответ на этот вопрос и документы для ответа.
По сути, единственная странная / неочевидная вещь, происходящая здесь, заключается в том, что вы должны понизить параметры до ожидаемого вами типа. Итак, в вашем случае, если вы издеваетесь над методом, который принимает один параметр "TestObject", то вам придется сделать что-то подобное в вашей реализации "ответа":
Object[] args = invocation.getArguments();
TestObject testObj = (TestObject) args[0];
if ("OK".equals(testObj.value)) {
return new ArrayList(value1, value2);
} else if ("NO".equals(testObj.value)) {
return new ArrayList(singleObject);
}