Mockito未完成存根异常

31

我是Mockito的新手,我已经尝试查找有关这个异常的信息,但我没有找到确切的答案。 当我同时使用两个mock时,我的代码会出现此异常,这意味着我通过一个mock的构造函数传递了另一个mock。像这样:

...
OperationNode child = getNode(Operation.ADD);
child.insertNode(getConstantNode(getIntegerValue(2));
...

 private ConstantNode getConstantNode(NumericalValue value){
    ConstantNode node = Mockito.mock(ConstantNode.class);
    Mockito.when(node.evaluate()).thenReturn(value);
    Mockito.when(node.toString()).thenReturn(value.toString());
    return node;
}

private IntegerValue getIntegerValue(int number) {
   IntegerValue integerValue = Mockito.mock(IntegerValue.class);
   Mockito.when(integerValue.getValue()).thenReturn(number);
   Mockito.when(integerValue.toString()).thenReturn(Integer.toString(number));
   return integerValue;
}

在一个论坛上,我读到了关于不要通过另一个mock的构造函数发送mock的建议,因为Mockito可能会混淆mock调用,所以我尝试了以下代码:

NumericalValue value = getIntegerValue(2);
child.insertNode(getConstantNode(value));

但是没有用。我确认只有方法 toString()getValue() 被调用了,因为这些是类唯一拥有的方法。我不明白发生了什么。

我还尝试着分别使用模拟对象,以查看是否有做错什么:

child.insertNode(new ConstantNode(getIntegerValue(2)));

那个完美地运作了。

child.insertNode(getConstantNode(new IntegerValue(2)));

那也可以正常工作。


2
已知问题 https://code.google.com/p/mockito/issues/detail?id=53 - Sajan Chandran
5
可点击链接: https://code.google.com/p/mockito/issues/detail?id=53 - Duncan Jones
模拟返回模拟通常是代码异味。 - haventchecked
正如这里所解释的那样,我在使用PowerMockitomockStatic时遇到了异常,因为我忘记在我的测试类上添加@PrepareForTest({ClassToMock.class})注解。 - undefined
5个回答

39
根据我在 mockito 的 "Issue 53"(https://code.google.com/p/mockito/issues/detail?id=53)上所读到的内容,我的代码由于 Mockito 中涉及的验证框架而遇到了问题。具体来说,以下代码本身就引起了异常。
private ConstantNode getConstantNode(NumericalValue value){
    ConstantNode node = Mockito.mock(ConstantNode.class);
    Mockito.when(node.evaluate()).thenReturn(value);
    Mockito.when(node.toString()).thenReturn(value.toString());
    return node;
}

如果您还记得我的代码,那么参数值也是模拟的。因此,在调用thenReturn()时,我相信(如果我错了,请有人纠正我)验证框架会触发并确保每个“when”都已经被调用/验证/等等。所以,如果发生这种情况,Mockito.when(node.toString()).thenReturn(value.toString())将不会被验证,因为它还没有从value.toString()返回,而后者启动了整个“验证一切”的过程。
我是如何解决的:
private ConstantNode getConstantNode(NumericalValue value){
    ConstantNode node = Mockito.mock(ConstantNode.class);
    Mockito.when(node.evaluate()).thenReturn(value);

    String numberToString = value.toString();

    Mockito.when(node.toString()).thenReturn(numberToString);
    return node;
}

这样,它就可以得到验证。我认为这是完全的代码异味,因为我必须留下一条注释来解释为什么在代码中使用一个看似无用的中间变量。
谢谢你的帮助。

谢谢!终于我开始理解这个奇怪的问题了。 - Piotr Sobczyk

11

这个问题已经有一些好的解决方法了,但是对于仍然有困难理解的人来说,可以考虑Java调用所有这些方法的顺序。根据Java语言规范,Java在调用方法之前会从左到右评估每个参数:

  1. integerValue.getValue(),Mockito记录下来
  2. when,Mockito获取最后一次调用(即integer.getValue)并开始设置存根
  3. value.toString,这是Mockito记录的模拟调用
  4. thenReturn在存根上
Mockito抱怨正是因为模拟调用,即第三步,在第二步(when)之后但在第四步(thenReturn)之前发生,导致验证框架抱怨存根。Joy的答案将麻烦的第三步移动到第一步之前,这是可以的;Sajan则完全删除了该语句,这也是可以的。

2

我认为问题出在这一行上:

Mockito.when(node.toString()).thenReturn(value.toString());,出现在方法getConstantNode里。

尝试删除这一行代码并检查是否能正常运行。 或者你可以尝试做一些如下的修改:

int num = 2;
child.insertNode(getConstantNode(getIntegerValue(num), num);
...

 private ConstantNode getConstantNode(NumericalValue value){
    ConstantNode node = Mockito.mock(ConstantNode.class);
    Mockito.when(node.evaluate()).thenReturn(value);
    Mockito.when(node.toString()).thenReturn(Integer.toString(number));
    return node;
}

private IntegerValue getIntegerValue(int number) {
   IntegerValue integerValue = Mockito.mock(IntegerValue.class);
   Mockito.when(integerValue.getValue()).thenReturn(number);
   return integerValue;
}

那么“number”是从哪里来的呢? - AHungerArtist
它解决了异常问题,但我仍然不明白为什么会发生这种情况。我已经阅读了第一条评论中提供的问题链接,但我仍然无法理解。 - Chayemor

0
这个问题的另一个可能原因让我苦思冥想了很长时间。我在模拟多个方法调用时,其中一个缺少了thenReturn,但是Mockito生成的堆栈跟踪引用了第一个被模拟的调用(它确实有thenReturn),而不是真正出现问题的那个调用,所以我花了很长时间才发现问题。这种行为真的很令人困惑!
Mockito.when(service.method1()).thenReturn(value1); // stacktrace pointed here
Mockito.when(service.method2()).thenReturn(value2);
Mockito.when(service.method3()); // missing thenReturn()

0

我认为这是调用顺序和Mockito框架验证的问题。尝试这样做,看看是否有帮助:

...
OperationNode child = getNode(Operation.ADD);
IntegerValue value = getIntegerValue(2);
ConstantNode node =  Mockito.mock(ConstantNode.class);
Mockito.when(node.evaluate()).thenReturn(value);
Mockito.when(node.toString()).thenReturn(value.toString());
child.insertNode(node);

...

private IntegerValue getIntegerValue(int number) {
   IntegerValue integerValue = Mockito.mock(IntegerValue.class);
   Mockito.when(integerValue.getValue()).thenReturn(number);
   Mockito.when(integerValue.toString()).thenReturn(Integer.toString(number));
   return integerValue;
}

来源:https://code.google.com/p/mockito/issues/detail?id=53


这不是同样的事情吗,只是没有方法在其中起作用?无论如何,我尝试了一下,没有用。 - Chayemor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接