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

时间:2014-10-11 19:42:20

标签: java mocking mockito

我在运行测试时遇到异常。我正在使用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;
    }
}

3 个答案:

答案 0 :(得分:278)

你在模仿中嵌套嘲弄。在您完成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
  • thenReturn方法返回的OngoingStubbing对象上调用方法when

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

事实上,由于Mockito无法看到您的代码,您还可以按如下方式编写模拟:

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

这种风格的阅读不太清楚,特别是因为在这种情况下null必须进行转换,但它会产生与Mockito相同的交互序列,并且会获得与上面一行相同的结果。 / p>

然而,行

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

导致以下与Mockito的互动:

  1. 调用模拟方法mainModel.getList()
  2. 调用静态方法when
  3. 创建mock的新SomeModel getSomeList()内,
  4. 调用模拟方法model.getName()
  5. 此时Mockito感到困惑。它以为你在嘲笑mainModel.getList(),但现在你告诉它你要嘲笑model.getName()方法。对于Mockito来说,看起来你正在做以下事情:

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

    这对Mockito来说很愚蠢,因为它无法确定您使用mainModel.getList()做了什么。

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

    一般来说,如Mockito所做的那样依靠静态是一个糟糕的设计决策,因为它可能会导致违反最小惊讶原则的情况。然而,Mockito的设计确实会产生清晰而富有表现力的嘲弄,即使它有时会让人感到惊讶。

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

      

    3:你正在扼杀另一个模拟人员的行为之前&#39;然后返回&#39;指示完成后

答案 1 :(得分:0)

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
            }

答案 2 :(得分:0)

对于那些使用 com.nhaarman.mockitokotlin2.mock {}

解决方法 1

例如,当我们在另一个模拟中创建一个模拟时会发生此错误

mock {
    on { x() } doReturn mock {
        on { y() } doReturn z()
    }
}

对此的解决方案是在变量中创建子模拟,并在父模拟范围内使用该变量,以防止模拟创建被显式嵌套。

val liveDataMock = mock {
        on { y() } doReturn z()
}
mock {
    on { x() } doReturn liveDataMock
}

解决方法 2

确保所有应该有 thenReturn 的模拟都有 thenReturn

GL