Как работает вызов метода у мока в Mockito?

«Как работает вызов метода у мока в Mockito?» — вопрос из категории Тестирование, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Поведение зависит от конфигурации мока. Без настройки возвращаются default values.

1. Мок без конфигурации (возвращает default values):

List<String> mockList = mock(List.class);

// Default values:
System.out.println(mockList.get(0));     // null (для объектов)
System.out.println(mockList.size());     // 0 (для int)
System.out.println(mockList.isEmpty());  // false (для boolean)
System.out.println(mockList.iterator()); // null (для объектов)

2. Конфигурация возвращаемого значения:

// Фиксированное значение
when(mockList.get(0)).thenReturn("first");
when(mockList.get(1)).thenReturn("second");

// Последовательные возвращаемые значения
when(mockList.size())
    .thenReturn(1)
    .thenReturn(2)
    .thenReturn(3);

// Вызов с аргументами
when(mockList.get(anyInt())).thenReturn("any");
when(mockList.get(eq(5))).thenReturn("fifth");

3. Конфигурация исключений:

// Для не-void методов
when(mockList.get(0)).thenThrow(new RuntimeException("Error"));

// Для void методов
doThrow(new RuntimeException("Cleanup failed"))
    .when(mockList).clear();

// Последовательные исключения
when(mockList.isEmpty())
    .thenThrow(new RuntimeException("First"))
    .thenReturn(false);

4. Ответы на основе вызова (Answer):

when(mockList.get(anyInt())).thenAnswer(invocation -> {
    int index = invocation.getArgument(0);
    return "element-" + index;
});

when(mockList.add(anyString())).thenAnswer(invocation -> {
    String value = invocation.getArgument(0);
    return value.length() > 5;
});

5. Spy (частичный мок реального объекта):

List<String> realList = new ArrayList<>();
List<String> spyList = spy(realList);

// Реальные вызовы
spyList.add("real");

// Переопределенные вызовы
when(spyList.size()).thenReturn(100);

// doCallRealMethod() для вызова реальной реализации
doCallRealMethod().when(spyList).clear();

6. Верификация вызовов:

// Проверка количества вызовов
verify(mockList, times(2)).get(0);
verify(mockList, atLeastOnce()).add(anyString());
verify(mockList, never()).remove(0);

// Проверка порядка вызовов
InOrder inOrder = inOrder(mockList);
inOrder.verify(mockList).add("first");
inOrder.verify(mockList).add("second");

// Проверка аргументов
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mockList).add(captor.capture());
assertEquals("test", captor.getValue());

7. Продвинутые ArgumentMatchers:

// Комбинации матчеров
when(mockList.get(argThat(index -> index >= 0 && index < 10)))
    .thenReturn("valid");

// Кастомные матчеры
when(mockList.add(matches("[A-Z].*"))).thenReturn(true);

// Проверка с матчерами
verify(mockList).set(anyInt(), startsWith("prefix"));

Важные правила Mockito:

  1. Нельзя использовать матчеры вне stubbing/verification
  2. При использовании матчеров, все аргументы должны использовать матчеры
  3. Spy требует осторожности с final/private методами
  4. Моки не подменяют equals/hashCode по умолчанию