Mockito - I understand a spy calls the real methods on an object, while a mock calls methods on the double object. Also spies are to be avoided unless there is a code smell.
In short:
@Spy
and @Mock
are used heavily in testing of code, but developers do confuse in cases when to use one of them and thus developers end up in using @Mock
to be safe.
@Mock
when you want to just test the functionality externally
without actually calling that method.@Spy
when you want to test the functionality externally +
internally with the very method being called.Below is the example where i have taken the scenario of Election20xx in America.
Voters can be divided according to VotersOfBelow21
and VotersOfABove21
.
The ideal Exit poll says that Trump will win the election because VotersOfBelow21
and VotersOfABove21
both will vote for trump saying "We elected President Trump "
But this is not the real scenario:
Voters of both age group voted for Trump because they had No other effective choice other than Mr Trump.
So how do you Test it??
public class VotersOfAbove21 {
public void weElected(String myVote){
System.out.println("Voters of above 21 has no Choice Than Thrump in 20XX ");
}
}
public class VotersOfBelow21 {
public void weElected(String myVote){
System.out.println("Voters of below 21 has no Choice Than Thrump in 20XX");
}
}
public class ElectionOfYear20XX {
VotersOfAbove21 votersOfAbove21;
VotersOfBelow21 votersOfBelow21;
public boolean weElected(String WeElectedTrump){
votersOfAbove21.weElected(WeElectedTrump);
System.out.println("We elected President Trump ");
votersOfBelow21.weElected(WeElectedTrump);
System.out.println("We elected President Trump ");
return true;
}
}
Now Note in above first two classes, both the age group people says that they do not have a better choice than trump. Which explicitly means that they voted for Trump just because they had no choice.
Now the ElectionOfYear20XX
says that Trump Won because both age group voted for him overwhelmingly.
If we were to Test the ElectionOfYear20XX
with @Mock, then we might not be able to get the real reason why Trump won, we will be just testing the external reason.
If we test the ElectionOfYear20XX
with @Spy, then we get the real reason why Trump won with the external exit poll results, i.e internally + externally.
Our ELectionOfYear20XX_Test
class:
@RunWith(MockitoJUnitRunner.class)
public class ELectionOfYear20XX_Test {
@Mock
VotersOfBelow21 votersOfBelow21;
@Mock
VotersOfAbove21 votersOfAbove21;
@InjectMocks
ElectionOfYear20XX electionOfYear20XX;
@Test
public void testElectionResults(){
Assert.assertEquals(true,electionOfYear20XX.weElected("No Choice"));
}
}
This should output just the logic test results i.e. external check:
We elected President Trump
We elected President Trump
Testing with @Spy
externally as well as internally with actual method invocation.
@RunWith(MockitoJUnitRunner.class)
public class ELectionOfYear20XX_Test {
@Spy
VotersOfBelow21 votersOfBelow21;
@Spy
VotersOfAbove21 votersOfAbove21;
@InjectMocks
ElectionOfYear20XX electionOfYear20XX;
@Test
public void testElectionResults(){
Assert.assertEquals(true,electionOfYear20XX.weElected("No Choice"));
}
}
Output:
Voters of above 21 has no Choice Than Thrump in 20XX
We elected President Trump
Voters of below 21 has no Choice Than Thrump in 20XX
We elected President Trump