1. 概述
在本教程中,我们将讨论当我们使用Mockito时可能会遇到的一个常见错误。错误消息是:
Wanted but not invoked:
// class name and location
Actually, there were zero interactions with this mock.
让我们理解这个错误可能的来源,并学习如何解决它。
2. 示例设置
首先,我们创建一个稍后将要模拟的类。它包含一个单一的方法,总是返回字符串"Baeldung":
class Helper {
String getBaeldungString() {
return "Baeldung";
}
}
现在,我们创建主类。它在类级别上声明了一个Helper实例。在单元测试中,我们将希望模拟这个实例:
class Main {
Helper helper = new Helper();
String methodUnderTest(int i) {
if (i > 5) {
return helper.getBaeldungString();
}
return "Hello";
}
}
此外,我们定义了一个方法,它接受一个Integer参数并返回:
- 如果Integer大于5,调用*getBaeldunString()*的结果
- 如果Integer小于或等于5,返回一个常量
3. 实际方法被调用而非模拟
现在尝试编写一个针对方法的单元测试。我们将使用@Mock注解来创建一个模拟的Helper。我们还会使用MockitoAnnotations.openMocks()启用Mockito注解。在测试方法中,我们将使用参数7调用*methodUnderTest()并检查它是否委托给getBaeldungString()*:
class MainUnitTest {
@Mock
Helper helper;
Main main = new Main();
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void givenValueUpperThan5_WhenMethodUnderTest_ThenDelegatesToHelperClass() {
main.methodUnderTest(7);
Mockito.verify(helper)
.getBaeldungString();
}
}
现在运行我们的测试:
Wanted but not invoked:
helper.getBaeldungString();
-> at com.baeldung.wantedbutnotinvocked.Helper.getBaeldungString(Helper.java:6)
Actually, there were zero interactions with this mock.
问题在于我们调用了构造函数来创建一个Main对象。因此,Helper实例是由new()调用创建的。**结果,我们使用了实际的Helper对象而不是模拟。*要解决问题,我们需要在Main*对象创建上方添加@InjectMocks:
@InjectMocks
Main main = new Main();
顺便提一下,如果我们任何时候在*methodUnderTest()*中用真实对象替换模拟实例,我们又会回到同样的问题:
String methodUnderTest(int i) {
helper = new Helper();
if (i > 5) {
return helper.getBaeldungString();
}
return "Hello";
}
总之,这里有两个需要注意的点:
- 模拟对象需要正确创建并注入。
- 在任何时刻都不能用其他对象替换模拟对象。
4. 方法未被调用
现在我们将编写一个新的单元测试。它将检查将3作为参数传递给*methodUnderTest()时,是否调用了getBaeldungString()*:
@Test
void givenValueLowerThan5_WhenMethodUnderTest_ThenDelegatesToGetBaeldungString() {
main.methodUnderTest(3);
Mockito.verify(helper)
.getBaeldungString();
}
再次运行测试:
Wanted but not invoked:
helper.getBaeldungString();
-> at com.baeldung.wantedbutnotinvocked.Helper.getBaeldungString(Helper.java:6)
Actually, there were zero interactions with this mock.
这次,仔细阅读错误消息。它说我们没有与模拟对象交互。现在,让我们检查方法的规范:3小于5,所以methodUnderTest()返回一个常量而不是委托给getBaeldungString()。**因此,我们的测试与规范相矛盾。
在这种情况下,我们有两种可能的结论:
- 规范是正确的:我们需要修复测试,因为验证是没有意义的。
- 测试是正确的:我们的代码中有一个需要解决的错误。
5. 总结
在本文中,我们在没有与模拟对象交互的情况下调用了Mockito.verify(),得到了一个错误。我们指出需要正确地注入和使用模拟对象。我们也看到了当测试不一致时,这个错误会出现。
如往常一样,代码可在GitHub上找到。