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);
}
Другие вопросы по тегам