Java 在 Mockito 中检测到未完成的存根

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26318569/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 02:13:18  来源:igfitidea点击:

Unfinished Stubbing Detected in Mockito

javamockingmockito

提问by Royal Rose

I am getting following exception while running the tests. I am using Mockito for mocking. The hints mentioned by Mockito library are not helping.

我在运行测试时遇到以下异常。我正在使用 Mockito 进行嘲笑。Mockito 库提到的提示没有帮助。

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
    -> at com.a.b.DomainTestFactory.myTest(DomainTestFactory.java:355)

    E.g. thenReturn() may be missing.
    Examples of correct stubbing:
        when(mock.isOk()).thenReturn(true);
        when(mock.isOk()).thenThrow(exception);
        doThrow(exception).when(mock).someVoidMethod();
    Hints:
     1. missing thenReturn()
     2. you are trying to stub a final method, you naughty developer!

        at a.b.DomainTestFactory.myTest(DomainTestFactory.java:276)
        ..........

Test Code from DomainTestFactory. When I run the following test, I see the exception.

测试代码来自DomainTestFactory. 当我运行以下测试时,我看到了异常。

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); // Line 355
}

private List<SomeModel> getSomeList() {
    SomeModel model = Mockito.mock(SomeModel.class);
    Mockito.when(model.getName()).thenReturn("SomeName"); // Line 276
    Mockito.when(model.getAddress()).thenReturn("Address");
    return Arrays.asList(model);
}

public class SomeModel extends SomeInputModel{
    protected String address;
    protected List<SomeClass> properties;

    public SomeModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    public String getAddress() {
        return this.address;
    }

}

public class SomeInputModel{

    public NetworkInputModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    protected String Name;
    protected List<SomeClass> properties;

    public String getName() {
        return this.Name;
    }

    public void setName(String value) {
        this.Name = value;
    }
}

采纳答案by Luke Woodward

You're nesting mocking inside of mocking. You're calling getSomeList(), which does some mocking, before you've finished the mocking for MyMainModel. Mockito doesn't like it when you do this.

你在嘲讽中嵌套嘲讽。getSomeList()在完成对 的模拟之前,您正在调用,它会进行一些模拟MyMainModel。当你这样做时,Mockito 不喜欢它。

Replace

代替

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); --> Line 355
}

with

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    List<SomeModel> someModelList = getSomeList();
    Mockito.when(mainModel.getList()).thenReturn(someModelList);
}

To understand why this causes a problem, you need to know a little about how Mockito works, and also be aware in what order expressions and statements are evaluated in Java.

要理解为什么这会导致问题,您需要对 Mockito 的工作原理有所了解,还需要了解在 Java 中表达式和语句的计算顺序。

Mockito can't read your source code, so in order to figure out what you are asking it to do, it relies a lot on static state. When you call a method on a mock object, Mockito records the details of the call in an internal list of invocations. The whenmethod reads the last of these invocations off the list and records this invocation in the OngoingStubbingobject it returns.

Mockito 无法读取您的源代码,因此为了弄清楚您要求它做什么,它在很大程度上依赖于静态。当您在模拟对象上调用方法时,Mockito 会在内部调用列表中记录调用的详细信息。该when方法从列表中读取这些调用中的最后一个,并将此调用记录在OngoingStubbing它返回的对象中。

The line

线

Mockito.when(mainModel.getList()).thenReturn(someModelList);

causes the following interactions with Mockito:

导致与 Mockito 的以下交互:

  • Mock method mainModel.getList()is called,
  • Static method whenis called,
  • Method thenReturnis called on the OngoingStubbingobject returned by the whenmethod.
  • mainModel.getList()调用模拟方法,
  • 静态方法when被调用,
  • 在方法返回thenReturnOngoingStubbing对象上调用when方法。

The thenReturnmethod can then instruct the mock it received via the OngoingStubbingmethod to handle any suitable call to the getListmethod to return someModelList.

thenReturn然后,该方法可以指示它通过该OngoingStubbing方法接收到的模拟处理getList对返回方法的任何合适调用someModelList

In fact, as Mockito can't see your code, you can also write your mocking as follows:

事实上,由于 Mockito 看不到你的代码,你也可以这样写你的 mocking:

mainModel.getList();
Mockito.when((List<SomeModel>)null).thenReturn(someModelList);

This style is somewhat less clear to read, especially since in this case the nullhas to be casted, but it generates the same sequence of interactions with Mockito and will achieve the same result as the line above.

这种样式不太好读,特别是因为在这种情况下null必须强制转换,但它会生成与 Mockito 相同的交互序列,并将获得与上一行相同的结果。

However, the line

然而,该行

Mockito.when(mainModel.getList()).thenReturn(getSomeList());

causes the following interactions with Mockito:

导致与 Mockito 的以下交互:

  1. Mock method mainModel.getList()is called,
  2. Static method whenis called,
  3. A new mockof SomeModelis created (inside getSomeList()),
  4. Mock method model.getName()is called,
  1. mainModel.getList()调用模拟方法,
  2. 静态方法when被调用,
  3. 创建了一个新mockSomeModel(内部getSomeList()),
  4. model.getName()调用模拟方法,

At this point Mockito gets confused. It thought you were mocking mainModel.getList(), but now you're telling it you want to mock the model.getName()method. To Mockito, it looks like you're doing the following:

在这一点上,Mockito 感到困惑。它以为你在嘲笑mainModel.getList(),但现在你告诉它你想嘲笑这个model.getName()方法。对于 Mockito,您似乎正在执行以下操作:

when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);

This looks silly to Mockitoas it can't be sure what you're doing with mainModel.getList().

这看起来很傻,Mockito因为它不能确定你在做什么mainModel.getList()

Note that we did not get to the thenReturnmethod call, as the JVM needs to evaluate the parameters to this method before it can call the method. In this case, this means calling the getSomeList()method.

请注意,我们没有进入thenReturn方法调用,因为 JVM 需要先评估此方法的参数,然后才能调用该方法。在这种情况下,这意味着调用该getSomeList()方法。

Generally it is a bad design decision to rely on static state, as Mockito does, because it can lead to cases where the Principle of Least Astonishment is violated. However, Mockito's design does make for clear and expressive mocking, even if it leads to astonishment sometimes.

通常,依赖静态状态是一个糟糕的设计决策,就像 Mockito 所做的那样,因为它可能导致违反最小惊讶原则的情况。然而,Mockito 的设计确实提供了清晰而富有表现力的嘲讽,即使它有时会让人感到惊讶。

Finally, recent versions of Mockito add an extra line to the error message above. This extra line indicates you may be in the same situation as this question:

最后,最近版本的 Mockito 在上面的错误消息中添加了额外的一行。此额外行表示您可能与此问题处于相同的情况:

3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed

3:如果完成,您正在'thenReturn'指令之前存根内部另一个模拟的行为

回答by takharsh

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
E.g. thenReturn() may be missing.

For mocking of void methods try out below:

为了模拟 void 方法,请尝试以下方法:

//Kotlin Syntax

 Mockito.`when`(voidMethodCall())
           .then {
                Unit //Do Nothing
            }