解决Mockito异常:需要但未调用

2023/06/15

1. 概述

在本教程中,我们将讨论使用Mockito时可能遇到的常见错误。异常消息是:

Wanted but not invoked:
// class name and location
Actually, there were zero interactions with this mock.

让我们了解此错误的潜在来源以及如何修复它。

2. 示例设置

首先,让我们创建我们稍后要mock的类。它包含一个单独的方法,始终返回“Tuyucheng”:

class Helper {
    String getTuyuchengString() {
        return "Tuyucheng";
    }
}

现在让我们创建我们的主类。它在类级别声明一个Helper实例,我们希望在单元测试期间mock这个实例

class Main {
    Helper helper = new Helper();

    String methodUnderTest(int i) {
        if (i > 5) {
            return helper.getTuyuchengString();
        }
        return "Hello";
    }
}

最重要的是,我们定义了一个接收Integer作为参数并返回的方法:

  • 如果Integer大于5,调用getTuyuchengString()的结果
  • 如果Integer小于或等于5,则为常量

3. 调用真正的方法而不是Mock

让我们尝试为我们的方法编写单元测试,我们将使用@Mock注解来创建mock的Helper。我们还将调用MockitoAnnotations.openMocks()来启用Mockito注解。在测试方法中,我们将使用参数7调用methodUnderTest()并检查它是否委托给getTuyuchengString():

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)
                .getTuyuchengString();
    }
}

现在让我们运行我们的测试:

Wanted but not invoked:
helper.getTuyuchengString();
-> at cn.tuyucheng.taketoday.wantedbutnotinvocked.Helper.getTuyuchengString(Helper.java:6)
Actually, there were zero interactions with this mock.

问题是我们调用了构造函数来实例化一个Main对象。因此,Helper实例是通过调用new()创建的。因此,我们使用的是真正的Helper对象而不是我们的mock对象。要解决这个问题,我们需要在创建Main对象的基础上添加@InjectMocks

@InjectMocks
Main main = new Main();

作为旁注,如果我们在methodUnderTest()的任何点用真实对象替换mock实例,我们将再次陷入同样的问题:

String methodUnderTest(int i) {
    helper = new Helper();
    if (i > 5) {
        return helper.getTuyuchengString();
    }
    return "Hello";
}

简而言之,我们这里有两个注意点:

  • 应该正确创建和注入mock
  • 在任何时候都不应将mock对象替换为其他对象

4. 方法未被调用

我们现在将编写一个新的单元测试,它将检查将3作为参数传递给methodUnderTest()是否会调用getTuyuchengString():

@Test
void givenValueLowerThan5_WhenMethodUnderTest_ThenDelegatesToGetTuyuchengString() {
    main.methodUnderTest(3);
    Mockito.verify(helper)
       .getTuyuchengString();
}

再一次,我们可以运行测试:

Wanted but not invoked:
helper.getTuyuchengString();
-> at cn.tuyucheng.taketoday.wantedbutnotinvocked.Helper.getTuyuchengString(Helper.java:6)
Actually, there were zero interactions with this mock.

这次,让我们仔细阅读错误信息。它说我们没有与mock交互。现在让我们回顾一下我们的方法规范:3小于5,因此methodUnderTest()返回一个常量而不是委托给getTuyuchengString()。因此,我们的测试与规范相矛盾

在这种情况下,我们只有两种可能的结论:

  • 规范是正确的:我们需要修复我们的测试,因为验证是无用的。
  • 测试是正确的:我们的代码中有一个错误需要解决。

5. 总结

在本文中,我们在没有与mock交互的情况下调用Mockito.verify()并出现错误。我们指出我们需要正确地注入和使用mock。我们还看到这个错误是由不连贯的测试引起的。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章