我在运行测试时得到以下异常。我用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;
    }
}

你是在嘲讽中嵌套嘲讽。您正在调用getSomeList(),它在完成MyMainModel的模拟之前执行一些模拟。莫奇托不喜欢你这么做。

取代

@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);
}

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

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

这条线

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

导致以下与Mockito的交互:

模拟方法mainModel.getList()被调用, 静态方法, 方法thenReturn在when方法返回的OngoingStubbing对象上调用。

然后,thenReturn方法可以指示它通过ongoingstub方法接收到的mock来处理任何对getList方法的适当调用,以返回someModelList。

事实上,因为Mockito不能看到你的代码,你也可以这样写你的mock:

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

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

然而,这一行

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

导致以下与Mockito的交互:

模拟方法mainModel.getList()被调用, 静态方法, 创建一个新的SomeModel模拟(在getSomeList()中), 模拟方法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'指令完成之前,你正在stub另一个mock内部的行为


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
            }

对于那些使用com.nhaarman.mockitokotlin2的人。模拟{}

解决方案1

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

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

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

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

解决方案2

确保你所有的mock都有一个thenReturn。

GL


(abc服务)

检查语法:

doThrow(new RunTimeException()).when(abcService).add(any(), any())

常见错误如下:

A. doThrow(new RunTimeException()).when(abcService.add(any(), any()))

类似地,检查when().then return(),等等。


@Luke Woodward的详细回答让我非常兴奋,他想分享一个解决方案。 正如卢克·伍德沃德解释的那样,我们不能有两个电话

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

比可以发生在调用链中。 但如果你要使用结构:

doReturn(mockToken("token3")).when(mock).getAccessToken();

when

OAuth2AccessToken mockToken(String tokenVal){
        OAuth2AccessToken token = Mockito.mock(OAuth2AccessToken.class);
        doReturn( 60 ).when(token).getExpiresIn();
        doReturn(tokenVal).when(token).getValue();
        return token;
    }

一切都会像预期的那样进行。