【问题标题】:Mockito gives UnfinishedVerificationException when it seems OKMockito 在看起来没问题时给出 UnfinishedVerificationException
【发布时间】:2013-04-01 00:56:26
【问题描述】:

当我认为我已正确完成所有操作时,Mockito 似乎正在抛出 UnfinishedVerificationException。这是我的部分测试用例:

HttpServletRequest req = mock(HttpServletRequest.class);
when(req.getHeader("Authorization")).thenReturn("foo");

HttpServletResponse res = mock(HttpServletResponse.class);

classUnderTest.doMethod(req, res); // Use the mock

verify(res, never());
verify(req).setAttribute(anyString(), anyObject());

这里是部分类和方法:

class ClassUnderTest extends AnotherClass {
    @Override
    public String doMethod(ServletRequest req, ServletRequest res) {
        // etc.
        return "someString";
    }
}

忽略你永远不应该模拟你不拥有的接口这一事实,为什么 Mockito 会给我以下信息?

org.mockito.exceptions.misusing.UnfinishedVerificationException: 
Missing method call for verify(mock) here:
-> at (redacted)

Example of correct verification:
    verify(mock).doSomething()

Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.

at [test method name and class redacted]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
... etc

【问题讨论】:

  • classUnderTest 的类是什么样子的?方法的签名是什么?它适用于我的公共非最终类/方法。
  • 我已经用 ClassUnderTest 更新了问题
  • 消息中引用的行(您已编辑)是否与实际引发异常的行相同?通常,如果您有未完成的验证,Mockito 直到您下次使用 Mockito 方法时才会报告它。您的问题很可能出在此之前的测试方法中。这就是 Mockito 报告错误发生的行号与堆栈跟踪本身分开的原因。
  • 谢谢你,大卫。这就是问题所在。您会将您的评论复制到答案中以便我接受吗?

标签: unit-testing junit mockito junit5


【解决方案1】:

如果您尝试 verify 一个需要带有 any() 的原始参数的方法,也可能会导致这种情况:

例如,如果我们的方法有这个签名:

method(long l, String s);

如果您尝试像这样验证它,它将失败并显示上述消息:

verify(service).method(any(), anyString());

把它改成anyLong()就可以了:

verify(service).method(anyLong(), anyString());

【讨论】:

  • 为什么不通过 anyString()
  • @IgorGanapolsky 谢谢,改为anyString()
  • 投了赞成票,因为这就是发生在我身上的事。错误消息没有提到 any() 不能匹配原语。
  • 这正是发生在我身上的事
  • 谢谢!这解决了我的问题。错误信息确实被误导了。
【解决方案2】:

我刚刚遇到了这个我自己,它给我带来了很多困惑。

正如 David 上面提到的那样,Mockito 在 next Mockito 方法调用中报告错误,这些错误可能不在同一个测试方法中。虽然异常消息确实包含对发生错误的实际位置的引用,但我发现不正确的测试失败对测试过程产生反作用。而且测试越简单,错误就越有可能出现在下一个测试中!

这是一个简单的修复方法,可确保错误出现在正确的测试方法中:

@After
public void validate() {
    validateMockitoUsage();
}

来自 Mockito 文档here

如果您滥用 Mockito,它会引发异常,以便您知道您的 测试编写正确。 问题是 Mockito 做到了 下次使用框架时进行验证(例如,下次验证时, 存根,调用模拟等)。但即使可能抛出异常 在下一个测试中,异常消息包含一个可导航的堆栈 带有缺陷位置的微量元素。因此,您可以单击并 找到 Mockito 被滥用的地方。

但有时,您可能 想要明确地验证框架的使用。例如,其中之一 用户想将 validateMockitoUsage() 放在他的 @After 方法中,所以 当他滥用 Mockito 时,他立即知道。没有它,他 下次他使用 框架。 在@After 中使用 validateMockitoUsage() 的另一个好处 是 jUnit runner 在有缺陷的测试方法中总是会失败 而普通的“下一次”验证可能无法通过下一次测试 方法。但即使 JUnit 可能将下一个测试报告为红色,也不要 担心它,只需单击中的可导航堆栈跟踪元素 异常信息即时定位你误用的地方 模仿。

【讨论】:

【解决方案3】:

由于使用带有boolean 参数的any(),我遇到了同样的错误,而显然它需要是anyBoolean()

【讨论】:

    【解决方案4】:

    就我而言,使用 kotlin 是因为要测试的函数未声明为 open

    异常通知不能使用final/private/equals/hash 方法。

    fun increment(){
            i++
        }
    

    open fun increment(){
            i++
        }
    

    【讨论】:

      【解决方案5】:

      使用 Junit 5,您可以添加以下内容以在控制台中显示更有意义的 Mockito 异常

      @AfterEach
      public void validate() {
          validateMockitoUsage()
      }
      

      另请参阅此答案:https://stackoverflow.com/a/22550055/8073652

      【讨论】:

        【解决方案6】:

        对我来说,问题原来是测试上下文 xml 中缺少 bean 声明。它用于另一个类使用的自定义方面类,该类的实例是类的构造函数的参数,该类的构造函数是失败的 verify() 调用的参数。所以我将 bean 声明添加到上下文 xml 中,之后它工作得很好。

        【讨论】:

          【解决方案7】:

          MyRepository 类有类似的异常

          org.mockito.exceptions.misusing.UnfinishedVerificationException: 此处缺少验证(模拟)的方法调用: -> 在 MyRepository$$FastClassBySpringCGLIB$$de8d8358.invoke()

          正确验证示例:

          verify(mock).doSomething()
          

          当我为 MyRepository 创建接口和模拟接口时,问题已解决,但没有实现。 似乎 spring 创建了一些 CGLIB 代理,并导致 UnfinishedVerificationException 异常。

          【讨论】:

            【解决方案8】:

            在以下堆栈中我也遇到了同样的问题:

            • 科特林
            • Junit 4.13
            • Mockito 2.28.2 + Mockito-Inline 2.13.0
            • Robolectric 4.3.1

            我尝试验证 lambda 调用:

            @RunWith(RobolectricTestRunner::class)
            class MainViewTest {
            
                @get:Rule
                val mockitoRule: MockitoRule = MockitoJUnit.rule()
            
                @Mock private lateinit var mockClickCallback: () -> Unit
            
                @Test
                fun `should call clickCallback on the button click`() {
                    val activity = Robolectric.buildActivity(MainActivity::class.java).create().get()
                    val viewUnderTest = MainView(activity)
                    viewUnderTest.setClickCallback(mockClickCallback)
            
                    viewUnderTest.button.performClick()
            
                    verify(mockClickCallback).invoke() // UnfinishedVerificationException
                }
            }
            

            然后我在Github上找到the issue,看来问题出在Robolectric。我使用了以下解决方法:

            @RunWith(RobolectricTestRunner::class)
            class MainViewTest {
            
                private interface UnitFunction: () -> Unit
            
                @Test
                fun `should call clickCallback on the button click`() {
                    val activity = Robolectric.buildActivity(MainActivity::class.java).create().get()
                    val viewUnderTest = MainView(activity)
                    val mockClickCallback = mock(UnitFunction::class.java) as () -> Unit
                    viewUnderTest.setClickCallback(mockClickCallback)
            
                    viewUnderTest.button.performClick()
            
                    verify(mockClickCallback).invoke() // OK
                }
            }
            

            【讨论】:

              【解决方案9】:

              我遇到了类似的问题,我找到了解决这个问题的方法。您要验证的模拟对象尚未重置,因此您应该重置它。您可以在测试用例函数之前reset(mock),这可能会有所帮助。

              【讨论】:

                【解决方案10】:

                如果您尝试使用 Mockito.verify 验证私有或包私有方法,您将收到此错误。

                如果您不想使用 PowerMockito,您可以将您的方法设置为受保护,我建议您添加 @VisibleForTesting 标签:

                之前:

                void doSomething() {
                   //Some behaviour
                }
                

                之后:

                @VisibleForTesting
                protected void doSomething() {
                   //Some behaviour
                }
                

                【讨论】:

                  【解决方案11】:

                  我遇到了同样的错误

                  org.mockito.exceptions.misusing.UnfinishedVerificationException: 
                  Missing method call for verify(mock) here:
                  -at com.xxx.MyTest.testRun_Should_xxx_When_yyy(MyTest.java:127)
                  
                  Example of correct verification:
                      verify(mock).doSomething()
                  
                  Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
                  Those methods *cannot* be stubbed/verified.
                  Mocking methods declared on non-public parent classes is not supported.
                  
                  
                      at com.xxx.MyTest.validate(MyTest.java:132)
                      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
                      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                      at java.lang.reflect.Method.invoke(Method.java:498)
                      at org.junit.internal.runners.MethodRoadie.runAfters(MethodRoadie.java:145)
                      at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:99)
                  ...
                  

                  在我的情况下,产生错误是因为我在调用 Mockito.verify(...) 之前使用了 PowerMockito.verifyStatic(),然后我有将 PowerMockito.verifyStatic() 移动到最后一行(或删除它)。

                  发件人:

                  PowerMockito.verifyStatic();
                  Mockito.verify(myMock, Mockito.times(1)).myMockedMethod(anyString(), anyString(), anyString(), any(XXX.class), any(YYY.class), any(ZZZ.class));
                  

                  收件人:

                  Mockito.verify(myMock, Mockito.times(1)).myMockedMethod(anyString(), anyString(), anyString(), any(XXX.class), any(YYY.class), any(ZZZ.class)); 
                  PowerMockito.verifyStatic();
                  

                  【讨论】:

                    【解决方案12】:

                    以上两个答案建议在每次测试后使用validateMockitoUsage() 方法。

                    虽然这是正确的,但我发现使用 @ExtendWith(MockitoExtension.class) 注释您的课程 在 Junit 5 中,添加了一些不错的 Mockito 功能,同时提供了相同的效果。此外,它对我来说看起来也更干净。

                    我猜 Junit 4 @RunWith(MockitoJUnitRunner.class) 会给出类似的结果,但我没有测试它。

                    【讨论】:

                      【解决方案13】:

                      更改为@RunWith(PowerMockRunner.class),问题就消失了。 之前用的是@RunWith(MockitoJUnitRunner.class)

                      希望对某人有所帮助..

                      【讨论】:

                        【解决方案14】:

                        测试单独运行良好,但在运行集成测试套件时失败并出现 UnfinishedVerificationException。当我们使用 mockito 中的 verify() 并使用 @EnableRetry 时,就会出现问题。 解决方法是使用

                        public static <T> T unwrapAndVerify(T mock, VerificationMode mode) { return ((T) Mockito.verify(AopTestUtils.getTargetObject(mock), mode)); }

                        https://html.developreference.com/article/15741255/Mocked+Spring+%23Service+that+has+%23Retryable+annotations+on+methods+fails+with+UnfinishedVerificationException 中所述。

                        【讨论】:

                          【解决方案15】:

                          这是我的一粒盐! 我发现 MockitoHibernate Validation 之间存在冲突。 我的解决方案是将我的合同检查(@NotNull、@NotEmpty 等)与模拟测试分开。我还开始使用validateMockitoUsage() 来确保一切正常运行。

                          【讨论】:

                            【解决方案16】:

                            我不确定您的“classUnderTest”来自哪里,但请确保它是模拟的不是真实的。 我在下面的测试用例中遇到了同样的问题:

                            MyAgent rpc = new MyAgent("myNodeName");
                            ...
                            rpc.doSomething();
                            ...
                            PowerMockito.verifyPrivate(rpc).invoke("initPowerSwitch");
                            PowerMockito.verifyPrivate(rpc).invoke("init", "192.168.0.23", "b2", 3);
                            

                            但是对于以下测试用例它消失了:

                            MyAgent rpc = PowerMockito.spy(new MyAgent("myNodeName"));
                            ...
                            rpc.doSomething();
                            ...
                            PowerMockito.verifyPrivate(rpc).invoke("initPowerSwitch");
                            PowerMockito.verifyPrivate(rpc).invoke("init", "192.168.0.23", "b2", 3);
                            

                            注意,Object rpc 应该被 PowerMockito.spy(...) 模拟。

                            【讨论】:

                              【解决方案17】:

                              使用 mockStatic 方法并多次调用 Mockito.verify 时遇到相同的异常,但传递的是接口而不是实现类。

                              代码错误:

                              try (MockedStatic<Service> staticMock = Mockito.mockStatic(Service.class, Mockito.CALLS_REAL_METHODS)) {
                                staticMock.verify(() -> ServiceImpl.method()); // passed without errors
                                staticMock.verify(() -> ServiceImpl.method()); // throws UnfinishedVerificationException 
                              }
                              

                              固定代码:

                              try (MockedStatic<ServiceImpl> staticMock = Mockito.mockStatic(Service.class, Mockito.CALLS_REAL_METHODS)) {
                                staticMock.verify(() -> ServiceImpl.method());
                                staticMock.verify(() -> ServiceImpl.method());
                              }
                              

                              这显然是我的错误,但 UnfinishedVerificationException 消息没有帮助

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 2019-05-31
                                • 1970-01-01
                                • 1970-01-01
                                • 2017-05-04
                                • 2020-08-08
                                • 1970-01-01
                                • 2015-09-21
                                • 1970-01-01
                                相关资源
                                最近更新 更多