Spy in Mockito
Mockito is a Java framework used for mocking and unit testing Java applications. Internally, this framework uses the Reflection API. Mockito simplifies the process of testing by creating mock objects and avoiding external dependencies. It is used with other testing frameworks like JUnit or TestNG.
In this tutorial, we will learn about spies in Mockito.
Mockito Spy
A Spy is like a partial mock, which will track the interactions with the object like a mock. Additionally, it allows us to call all the normal methods of the object. Whenever we call a method of the spy object, the real method will be invoked(unless it is stubbed).
Creating Spy Using Mockito.spy() Method
In the code below, we have created a spy for an ArrayList. The Mockito.spy() method is used for this. The verify() method is used to verify whether the interaction took place or not. When the add() method is called on the spy, the actual method is invoked, and elements are added to the list. We can verify it by viewing the size of the list.
public class MockitoSpyDemo
{
@Test
public void test()
{
ArrayList<Integer> arrList = new ArrayList<>();
ArrayList<Integer> spyArrList = Mockito.spy(arrList);
//Adding Elements
spyArrList.add(5);
spyArrList.add(10);
spyArrList.add(15);
//Verifying interactions
Mockito.verify(spyArrList).add(5);
Mockito.verify(spyArrList).add(10);
Mockito.verify(spyArrList).add(15);
//Verifying that elements were actually added to the list
assertEquals(3, spyArrList.size());
}
}
Creating Spy using @Spy Annotation
We can also create a spy object by using the @Spy annotation. To enable Mockito annotations, we can either use the MockitoAnnotations.initMocks(this) method. Or we can use the built-in runner @RunWith(MockitoJUnitRunner.class). The examples for both of them are shown below.
Using MockitoAnnotations.initMocks(this)
public class MockitoSpyDemo
{
//Using @Spy Annotation
@Spy
ArrayList<Integer> spyArrList = new ArrayList<>();
@Before
public void init()
{
MockitoAnnotations.initMocks(this);
}
@Test
public void test()
{
spyArrList.add(5);
spyArrList.add(10);
spyArrList.add(15);
Mockito.verify(spyArrList).add(5);
Mockito.verify(spyArrList).add(10);
Mockito.verify(spyArrList).add(15);
assertEquals(3, spyArrList.size());
}
}
Using @RunWith(MockitoJUnitRunner.class)
@RunWith(MockitoJUnitRunner.class)
public class MockitoSpyDemo
{
//Using @Spy Annotation
@Spy
ArrayList<Integer> spyArrList = new ArrayList<>();
@Test
public void test()
{
spyArrList.add(5);
spyArrList.add(10);
spyArrList.add(15);
Mockito.verify(spyArrList).add(5);
Mockito.verify(spyArrList).add(10);
Mockito.verify(spyArrList).add(15);
assertEquals(3, spyArrList.size());
}
}
Stubbing a Spy
We can stub a Spy to override the normal behavior of a method. Let's override the default behavior of the contains() method of ArrayLists. Let's make this method always return false for the value 5.
public class MockitoSpyDemo
{
@Test
public void test()
{
ArrayList<Integer> spyArrList = Mockito.spy(new ArrayList<>());
spyArrList.add(5);
Mockito.verify(spyArrList).add(5);
assertEquals(true, spyArrList.contains(5));//Default normal behavior
Mockito.doReturn(false).when(spyArrList).contains(5);
assertEquals(false, spyArrList.contains(5));//Stubbed Behavior
}
}
Let's override the size() method of ArrayLists. It should always return 5 as the list size.
public class MockitoSpyDemo
{
@Test
public void test()
{
ArrayList<Integer> spyArrList = Mockito.spy(new ArrayList<>());
spyArrList.add(5);
Mockito.verify(spyArrList).add(5);
assertEquals(1, spyArrList.size());//Default Normal Behavior
Mockito.doReturn(5).when(spyArrList).size();
assertEquals(5, spyArrList.size());//Stubbed Behavior
}
}
Mock vs Spy in Mockito
Spies are known as partial mocks. Mocks are created from the Class of a type, whereas spies are created from the actual instances of the class. All the normal methods of the class are not executed in the case of mocks. Spies, on the other hand, will use the original behavior of the methods. The result of the execution of a method is also reflected on the spy object.
For example, in the code below, we have a mock and a spy. The interaction is verified by both the objects. But an element was added only to the spy list when the add() method was invoked. The size of the mock list was zero, indicating that the add() method didn't do anything.
public class MockitoSpyDemo
{
@Test
public void test()
{
//Creating a spy and a mock
ArrayList<Integer> spyArrList = Mockito.spy(new ArrayList<Integer>());
ArrayList<Integer> mockArrList = Mockito.mock(ArrayList.class);
//Adding an element to spy and mock
spyArrList.add(5);
mockArrList.add(5);
//Verifying the interaction
Mockito.verify(spyArrList).add(5);
Mockito.verify(mockArrList).add(5);
//verifying the size of the spy and the mock
assertEquals(1, spyArrList.size());
assertEquals(0, mockArrList.size());
}
}
Mockito NotAMockException
When using Mockito, the NotAMockException is encountered frequently. This exception is thrown when we try to use mock-specific methods on some other object. For example, in the code below, we created an ArrayList but it is not a mock. When we use the verify() method on it, the NotAMockException is thrown. As the name of the exception suggests, the array list is not a mock, and we cannot use the verify() method on it. The exception is also accompanied by some suggestions on how to use the method.
public class MockitoSpyDemo
{
@Test
public void test()
{
ArrayList<Integer> arrList = new ArrayList<>();
arrList.add(5);
Mockito.verify(arrList).add(5);//NotAMockException
}
}
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to verify() is of type ArrayList and is not a mock!
Make sure you place the parenthesis correctly!
See the examples of correct verifications:
verify(mock).someMethod();
verify(mock, times(10)).someMethod();
verify(mock, atLeastOnce()).someMethod();
at Demo.test(Demo.java:15)
Summary
The Mockito framework is used for unit testing of Java applications. In this tutorial, we learned about Spy objects of the Mockito framework. Unlike mocks, spies execute the real methods of the object, and the result of the method is also reflected in the object. We also learned about the NotAMockException, which is thrown if the Mockito methods are not used properly.