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上找到。