Stubbing Spies and Handling Exceptions
In this step, we will learn how to stub a spy to override its default behavior and understand how to handle exceptions that may occur when using Mockito.
Stubbing a Spy
Spies use real object methods by default, but sometimes we want to change this behavior for testing purposes. This is called "stubbing" the spy. Let's add a test method that demonstrates how to stub a spy:
@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);
}
Important Note About Stubbing
When stubbing spy methods, it's recommended to use doReturn()
, doThrow()
, doAnswer()
, etc., instead of when()
. This is because with spies, the when().thenReturn()
syntax might call the real method during stubbing, which can cause unexpected side effects.
For example:
// 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
Handling NotAMockException
If you try to use Mockito's verification methods on a regular object (not a mock or spy), you'll get a NotAMockException
. Let's add a test method to demonstrate this exception:
@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"));
}
}
Let's compile our updated code:
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
The code should compile without errors. In this step, we learned how to stub spy methods to override their default behavior and how to handle the NotAMockException
that occurs when using verification methods on regular objects.
In the next step, we will compare mocks and spies to understand their key differences.