Table of Contents
In this lesson on Spy in Mockito, we will see how Spies differ from Mocks and how are these used.
Adding to classpath, using Maven
The fastest way to add Mockito to your project is using Maven dependency.
1 2 3 4 5 6 7 8 |
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all --> <dependency> Â Â Â <groupId>org.mockito</groupId> Â Â Â <artifactId>mockito-all</artifactId> Â Â Â <version>1.9.5</version> </dependency> |
This dependency is simple enough and does not bring any additional or redundant libraries. See here for latest versions of the library.
Using Mockito Annotations
Now, to start using Mockito Annotations, we must enable them beforehand in our Test class. There are 2 ways this can be done. By annotating with MockitoJUnitRunner:
1 2 3 4 5 6 |
@RunWith(MockitoJUnitRunner.class) public class MockitoAnnotationTest { ... } |
Or, this can also be done manually programmatically in a em>@Before annotated method, like:
1 2 3 4 5 6 |
@Before public void init() { MockitoAnnotations.initMocks(this); } |
Spies in Mockito
When Mocks are created in Mockito, they are done from the class of a Type, not from the actual instance. This is not the case with Spies.
Spy works just like real instances, it will behave in the same way as a real instance does, just that a Spy is instrumented in a way that all interactions with it can be tracked, like a method call or value initialization. Let’s see what tracking of interaction means with an example:
1 2 3 4 5 6 7 8 9 10 |
@Test public void whenCreateSpy_thenCreate() { Map spyMap = Mockito.spy(new HashMap()); spyMap.put("awesome", "java2blog"); Mockito.verify(spyMap).put("awesome", "java2blog"); assertEquals(1, spyMap.size()); } |
Here:
- A Map Spy was made using static spy(…) method
- Then, a key-value pair was added to it
- After the element was added, we verified its interaction as element addition was done
- Note that a real instance of Map was made and we even verified it using assertEquals to check its size, it was actually 1.
Let’s do the same example again, using @Spy
annotation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Spy Map<String, String> mySpyMap = new HashMap<String, String>(); @Test public void whenCreateSpy_thenCreate() { mySpyMap.put("awesome", "java2blog"); mySpyMap.put("super", "java2blog"); Mockito.verify(mySpyMap).put("awesome", "java2blog"); Mockito.verify(mySpyMap).put("super", "java2blog"); assertEquals(2, mySpyMap.size()); } |
Not much difference in that. Do remember that as we show in the beginning of this lesson, we need to activate Mockito annotations using one of the methods shown in the beginning.
Stubbing a Spy
Now, let’s see how we can stub a Spy. Using stubbing, we can override the behavior of a method and modify its return values, just like what we do in Mocks.
In our example, we will override the behavior of size() method of Map interface:
1 2 3 4 5 6 7 8 9 10 11 |
@Test public void stubbedAMethod_WithSpy() { Map<String, String> spyMap = Mockito.spy(new HashMap<String, String>()); assertEquals(0, spyMap.size()); Mockito.doReturn(5).when(spyMap).size(); assertEquals(5, spyMap.size()); } |
Another example
Let’s say you have an Employee class. This employee class has an object of Address class.
Method under test:
We are going to test Employee class’s getEmployeeDetails method. Below is the method we are going to test.
1 2 3 4 5 6 |
public String getEmployeeDetails() { return id+"_"+name+"_"+address.getAddressDetails(); } |
Now in above method, let’s suppose Address’s getAddressDetails() method do a database call and fetch address details.You don’t want to do DB connectin in your test, so you can simply mock this method and test functionality of getEmployeeDetails().
Create a class named "Employee.java"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public class Employee { int id; String name; Address address; public Employee(int id, String name) { super(); this.id = id; this.name = name; } public String getEmployeeDetails() { return id+"_"+name+"_"+address.getAddressDetails(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } |
Create another class named "Adddress.java"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class Address { int houseNo; String street; public String getAddressDetails() { // let's say this is database operation return houseNo+"_"+street; } public int getHouseNo() { return houseNo; } public void setHouseNo(int houseNo) { this.houseNo = houseNo; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } } |
Create a test class named "EmployeeDetailsTest"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Spy; import static org.mockito.Mockito.*; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import junit.framework.Assert; public class EmployeeDetailsTest { Employee e1; Employee e2; @Spy Address address=new Address(); @Rule public MockitoRule rule = MockitoJUnit.rule(); @Before public void setup(){ e1=new Employee(1, "John"); e1.setAddress(address); e2=new Employee(2, "Martin"); e2.setAddress(address); when(address.getAddressDetails()).thenReturn("88_HighStreet"); } @Test public void testEmployeeDetails() { String e1Details=e1.getEmployeeDetails(); Assert.assertEquals("1_John_88_HighStreet", e1Details); String e2Details=e2.getEmployeeDetails(); Assert.assertEquals("2_Martin_88_HighStreet", e2Details); } } |
As you can see here, we have used @Spy with Address object.
1 2 3 |
when(address.getAddressDetails()).thenReturn("88_HighStreet"); |
Above lines mocks getAddressDetails() method which is database operation which we have successfully avoided using Mockito.
Mock vs Spy
When Mockito creates a mock – it does so from the Class of a Type, not from an actual instance. On the other hand, a spy will be an original instance. It will still behave in the same way as the normal instance – the only difference is that it will also be instrumented to track all the interactions with it. All the methods of a spy are real unless stubbed.