我正在使用Mockito来编写我的测试用例。我有一个包含函数countPerson(boolean)
的简单类,我有兴趣测试它:
public class School {
//School is a singleton class.
public void countPerson(boolean includeTeacher) {
if (includeTeacher) {
countIncludeTeacher();
return;
}
countOnlyStudents();
}
public void countIncludeTeacher() {...}
public void countOnlyStudents() {...}
}
在我的单元测试中,我想测试countPerson(boolean)
方法:
@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();
}
我有以下错误:
org.mockito.exceptions.verification.NeverWantedButInvoked: school.countIncludeTeacher(); 从未想过这里:(SchoolTest.java 20)
为什么0次验证不起作用?
答案 0 :(得分:7)
实际上,问题是每个verify
调用是在同一个spySchool
实例上进行的。让我解释一下:
@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
}
事情是,在最新的verify
中,它失败了,因为之前在间谍上调用了countIncludeTeacher
方法,并且该调用未被注销。
您可以使用verifyNoMoreInteractions
执行此操作,该reset验证对象没有更多交互。您也可以Thread Synchronization in the .Net Framework该对象。
但请注意,实际上并不推荐这样做,引用Mockito Javadoc:
一句话警告:一些做过很多经典的,期望运行验证模拟的用户往往会经常使用
verifyNoMoreInteractions()
,即使在每种测试方法中也是如此。建议不要在每种测试方法中使用verifyNoMoreInteractions()
。verifyNoMoreInteractions()
是交互测试工具包中的一个方便的断言。仅在相关时使用它。滥用它会导致过度指定,不易维护的测试。你可以在这里找到进一步阅读。
和
Smart Mockito用户几乎不使用此功能,因为他们知道这可能是测试不佳的迹象。通常,您不需要重置模拟,只需为每种测试方法创建新的模拟。
请考虑在冗长,过度指定的测试中编写简单,小巧且专注的测试方法,而不是
reset()
。 测试方法中间的第一个潜在代码气味为reset()
。这可能意味着你的测试太多了。按照你的测试方法的低语:“请保持我们小,并专注于单一行为”。
我肯定建议您将此测试分为两部分:一部用于true
个案,一部用于false
个案。
答案 1 :(得分:1)
它失败了,因为当您在测试中运行countIncludeTeacher()
时,您实际上会调用spySchool.countPerson(true)
。
这将分支到您的其他函数,重新编码为间谍对象的调用。
在当前的设置中,您要么必须验证它只被调用一次,要么在运行第二次测试之前重置间谍对象实例。