I am using Mockito to write my test case. I have a simple class which contains a function countPerson(boolean)
which I am interested to test:
public class School {
//School is a singleton class.
public void countPerson(boolean includeTeacher) {
if (includeTeacher) {
countIncludeTeacher();
return;
}
countOnlyStudents();
}
public void countIncludeTeacher() {...}
public void countOnlyStudents() {...}
}
In my unit test, I want to test the countPerson(boolean)
method:
@Test
public void testCountPerson() {
School mSchool = School.getInstance();
School spySchool = Mockito.spy(mSchool);
spySchool.countPerson(true);
//Verify the function countIncludeTeacher is invoked once
verify(spySchool).countIncludeTeacher();
//Until here things are working well.
spySchool.countPerson(false);
//ERROR HERE when I try to verify the function has 0 times invoke
verify(spySchool, times(0)).countIncludeTeacher();
}
I have the following error:
org.mockito.exceptions.verification.NeverWantedButInvoked: school.countIncludeTeacher(); Never wanted here: (SchoolTest.java 20)
Why does the 0 times verify not work?
Mockito verify() simple example It's the same as calling with times(1) argument with verify method. verify(mockList, times(1)). size(); If we want to make sure a method is called but we don't care about the argument, then we can use ArgumentMatchers with verify method.
Mockito verifies argument values in natural java style: by using an equals() method.
Actually, the problem is that each verify
call is made on the same spySchool
instance. Let me explain:
@Test
public void testCountPerson() {
School mSchool = School.getInstance();
School spySchool = Mockito.spy(mSchool); // a spy is created here, Mockito is listening in.
spySchool.countPerson(true); // real method is invoked
verify(spySchool).countIncludeTeacher(); // our spy identified that countIncludeTeacher was called
spySchool.countPerson(false); // real method is invoked
verify(spySchool, times(0)).countIncludeTeacher(); // our spy still identified that countIncludeTeacher was called, before it was called before
}
The thing is that in the latest verify
, it fails because the countIncludeTeacher
method was called on the spy before and that called was not deregistered.
You can do it using verifyNoMoreInteractions
, which verifies that the object had no more interactions. You could also reset the object.
But note that this is really not recommended, quoting Mockito Javadoc:
A word of warning: Some users who did a lot of classic, expect-run-verify mocking tend to use
verifyNoMoreInteractions()
very often, even in every test method.verifyNoMoreInteractions()
is not recommended to use in every test method.verifyNoMoreInteractions()
is a handy assertion from the interaction testing toolkit. Use it only when it's relevant. Abusing it leads to overspecified, less maintainable tests. You can find further reading here.
And
Smart Mockito users hardly use this feature because they know it could be a sign of poor tests. Normally, you don't need to reset your mocks, just create new mocks for each test method.
Instead of
reset()
please consider writing simple, small and focused test methods over lengthy, over-specified tests. First potential code smell isreset()
in the middle of the test method. This probably means you're testing too much. Follow the whisper of your test methods: "Please keep us small & focused on single behavior".
I would definitely suggest that you split this test in two: one for the true
case and one for the false
case.
It fails because you actually DO invoke countIncludeTeacher()
when you run spySchool.countPerson(true)
in your test.
This branches out to your other function, recoding as an invocation on the spy object.
In your current setup, you would either have to verify it was called only once, or reset your spy object instance before running the second test.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With