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


150

运行测试时出现以下异常。我正在使用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)
        ..........

来自的测试代码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;
    }
}

嗨,穆雷尼克,我已经用行号更新了帖子
皇家玫瑰

Answers:


370

您是在嘲笑内部嵌套嘲笑。getSomeList()在完成对的模拟之前,您正在呼叫,它会进行一些模拟MyMainModel。执行此操作时,Mockito不喜欢它。

更换

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

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

要了解为什么这会导致问题,您需要稍微了解Mockito的工作方式,并且还需要知道Java中表达式和语句按什么顺序求值。

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

线

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

导致与Mockito的以下交互:

  • 模拟方法mainModel.getList()被称为
  • 静态方法when被称为
  • 在方法返回thenReturnOngoingStubbing对象上调用when方法。

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

实际上,由于Mockito看不到您的代码,因此您还可以按以下方式编写模拟:

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

这种样式的可读性较差,特别是因为在这种情况下null必须强制转换,但是它与Mockito产生相同的交互序列,并且将获得与上述代码相同的结果。

但是,线

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

导致与Mockito的以下交互:

  1. 模拟方法mainModel.getList()被称为
  2. 静态方法when被称为
  3. mockSomeModel中创建了一个新的getSomeList()
  4. 模拟方法model.getName()被称为

此时,Mockito感到困惑。它以为您在嘲笑mainModel.getList(),但是现在您要告诉它您要嘲笑该model.getName()方法。对于Mockito来说,您似乎正在执行以下操作:

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

这看起来很愚蠢,Mockito因为无法确定您在做什么mainModel.getList()

注意,我们没有进行thenReturn方法调用,因为JVM需要在调用该方法之前评估该方法的参数。在这种情况下,这意味着调用该getSomeList()方法。

通常,像Mockito一样,依靠静态状态是一个错误的设计决策,因为它可能导致违反最小惊讶原则的情况。但是,Mockito的设计确实可以进行清晰而富有表现力的嘲讽,即使有时会令人惊讶。

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

3:在完成“ thenReturn”指令之前,您正在测试另一个模拟的行为


这个事实有什么解释吗?解决方案有效。而且我不明白为什么“就地”模拟创建不起作用。当您创建模拟并通过引用其他模拟传递创建的模拟时,它就可以工作。
Capacytron,2014年

1
极好的答案,非常喜欢!我很久才能自己找到它了
Dici 2015年

4
很好的回答卢克!用简单的话非常详细的解释。谢谢。
TomaszKalkosiński,2015年

1
太棒了 有趣的是,当我执行直接方法调用并缓慢调试时,它就起作用了。CGLIB $ BOUND的Attribute将获得值true,但是以某种方式花费一些时间。当我使用直接方法调用并在训练之前停止时(当...时),那么我看到值首先为false,后来变为true。如果为假,并且训练开始,则会发生此异常。
Michael Hegner

你让我今天一整天都感觉很好!这种错误使您浪费大量时间!我以为一开始与科特林有关
布朗克斯

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

对于模拟void方法,请尝试以下方法:

//Kotlin Syntax

 Mockito.`when`(voidMethodCall())
           .then {
                Unit //Do Nothing
            }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.