对于 Mockito 中的每个 `when` 调用,最好有一个 `verify` 调用吗?

Is it preferred to have a `verify` call for each `when` call in Mockito?

编写测试用例时,通常的模式是 setup > execute > verify。这导致单元测试看起来像这样:

@Test
void testSomething() {
    // Setup
    when(someMock.someMethod()).thenReturn("someValue");
    
    // Execute (the doSomething implementation would invoke someMock.someMethod)
    testee.doSomething();
        
    // Verify
    verify(someMock, times(1)).someMethod();
}

我的问题是,考虑到 call 何时会引发 UnnecessaryStubbingException 异常,是否有必要包含 verify 调用?

此问题仅适用于 times1 的情况,因为缺少 UnnecessaryStubbingException 仅意味着 someMethod 已被调用一次并使用正确的参数和在某些情况下,您可能想要验证 someMethod 是否已被调用 never。如果没有验证,测试将如下所示并实现相同的检查:

@Test
void testSomething() {
    // Setup
    when(someMock.someMethod()).thenReturn("someValue");
    
    // Execute (the doSomething implementation would invoke someMock.someMethod)
    testee.doSomething();
}

编辑:一位朋友指出 verifyNoMoreInteractions 依赖于您有 verify 个电话,所以我想考虑这一点很重要。

编辑 2:我已将标题中的“必要”更改为“首选”,因为我对每种方法的 pros/cons 与技术要求相比更感兴趣。

提前致谢!

如您所述,不需要 - 技术测试验证了此行为。因此,选择应该来自您团队的偏好和专业知识。我个人认为 具有明确的验证步骤 使您的测试更具可读性和可维护性。几个原因:

  • 你可以有更多的when调用,与验证逻辑无关,这将“隐藏”测试意图,例如
@Test
void testSomething() {
    // Setup
    when(someMock.someMethod()).thenReturn("someValue");
    when(someMock2.someMethod2()).thenReturn("someValue2");
    when(someMock3.someMethod3()).thenReturn("someValue3");
    
    // Execute (the doSomething implementation 
    // would invoke someMock.someMethod)
    testee.doSomething();

    // code reviewer - "hm? what do we really test here?"
}
  • 从 API 的角度来看,在 UnnecessaryStubbingExceptionwhen 中抛出异常并不明显 - 这可能会给偶尔错过该事实的代码 reader 带来混乱。拥有 verify 让几乎所有开发人员都清楚测试的意图,即使来自其他技术堆栈
  • 一些开发人员可能来自早期的 Mockito 版本,其中抛出 UnnecessaryStubbingException 不是这种情况
  • UnnecessaryStubbingException 仅在 Mockito 处于严格模式时才会引发。如果此设置更改为 lenient down the line,您将无法再依赖引发此异常。
  • 您可以创建不包括 verify 调用意味着后续 verifyNoMoreInteractions 调用将失败的场景。

如果您已经知道您的代码片段会抛出异常,最好有一个测试用例来覆盖它,如下所示

@Test(预期=UnnecessaryStubbingException.class)

没有任何断言或验证语句的测试用例(您显示的跳过验证的那个)没有任何意义。当方法具有 void return 类型时,对情况进行验证检查是一种很好的方法。

不,在 Mockito 中调用时不需要为每个添加 Verify。每个测试用例本质上都应该是确定性的。

要回答您的问题,在这种特殊情况下,首选方法是使用 verifyNoMoreInteractionsverify(somemock, never()).someMethod()

您不必总是需要验证。您也可以使用 assertions。但是,一个测试用例必须有其中之一

例如。如果您确定某些方法将抛出异常。


@Test(expected = HeyIAmException.class)
void testSomething() {
     classUnderTest.someMethodWhichThrowsException();
}

或在 Junit 5 中。

@Test
void testSomething() {
     when(someMock.someMethod()).thenReturn("someValue");
     HeyIAmException exception = assertThrows(HeyIAmException.class, () ->
                    classUnderTest.someMethodWhichThrowsException()
     );
    
     assertEquals(exception.getCode(),rightCode);
}

在你的方法中,除了 Udalmik 的回答之外,我还添加了以下更多行。

  • 我们永远不会知道哪个存根方法导致了异常,因为任何 someMethod 都可能抛出异常。
  • 测试的意图不是很明确