Заглушки для шпионов (Spies) и обработка исключений
На этом этапе мы узнаем, как задавать поведение шпиона (stubbing), чтобы переопределить его поведение по умолчанию, и научимся обрабатывать исключения, которые могут возникнуть при использовании Mockito.
Задача поведения шпиона (Stubbing a Spy)
По умолчанию шпионы используют методы реальных объектов, но иногда мы хотим изменить это поведение в целях тестирования. Это называется "задачей поведения" (stubbing) шпиона. Добавим тестовый метод, который демонстрирует, как задавать поведение шпиона:
@Test
public void testStubbingSpy() {
// Create a spy of ArrayList
ArrayList<Integer> spyList = Mockito.spy(new ArrayList<>());
// Add an element
spyList.add(5);
// Verify the element was added
Mockito.verify(spyList).add(5);
// By default, contains() should return true for element 5
assertTrue(spyList.contains(5));
System.out.println("Stubbing Example - Default behavior: spyList.contains(5) = " + spyList.contains(5));
// Stub the contains() method to always return false for the value 5
Mockito.doReturn(false).when(spyList).contains(5);
// Now contains() should return false, even though the element is in the list
assertFalse(spyList.contains(5));
System.out.println("Stubbing Example - After stubbing: spyList.contains(5) = " + spyList.contains(5));
// The element is still in the list (stubbing didn't change the list content)
assertEquals(1, spyList.size());
assertEquals(Integer.valueOf(5), spyList.get(0));
System.out.println("Stubbing Example - Spy List Content: " + spyList);
}
Важное примечание о задаче поведения (Stubbing)
При задаче поведения методов шпиона рекомендуется использовать doReturn(), doThrow(), doAnswer() и т. д., вместо when(). Это связано с тем, что при использовании шпионов синтаксис when().thenReturn() может вызвать реальный метод при задаче поведения, что может привести к неожиданным побочным эффектам.
Например:
// This might call the real get() method, causing issues if index 10 doesn't exist
Mockito.when(spyList.get(10)).thenReturn(99); // May throw IndexOutOfBoundsException
// This is the correct way to stub a spy
Mockito.doReturn(99).when(spyList).get(10); // Safe, doesn't call the real method
Обработка исключения NotAMockException
Если вы попытаетесь использовать методы проверки (verification) Mockito на обычном объекте (не моке или шпионе), вы получите исключение NotAMockException. Добавим тестовый метод, чтобы продемонстрировать это исключение:
@Test
public void testNotAMockException() {
try {
// Create a regular ArrayList (not a mock or spy)
ArrayList<Integer> regularList = new ArrayList<>();
regularList.add(5);
// Try to verify an interaction on a regular object
Mockito.verify(regularList).add(5);
fail("Expected NotAMockException was not thrown");
} catch (NotAMockException e) {
// Expected exception
System.out.println("NotAMockException Example - Caught expected exception: " + e.getMessage());
assertTrue(e.getMessage().contains("Argument passed to verify() is not a mock"));
}
}
Скомпилируем обновленный код:
cd ~/project
javac -cp lib/junit.jar:lib/mockito-core.jar:lib/byte-buddy.jar:lib/byte-buddy-agent.jar:lib/objenesis.jar:lib/hamcrest-core.jar MockitoSpyDemo.java
Код должен скомпилироваться без ошибок. На этом этапе мы узнали, как задавать поведение методов шпиона, чтобы переопределить их поведение по умолчанию, и как обрабатывать исключение NotAMockException, которое возникает при использовании методов проверки на обычных объектах.
На следующем этапе мы сравним моки и шпионы, чтобы понять их основные различия.