在莫基托中检测到未完成的存根



我在运行测试时遇到以下异常。我正在使用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的嘲笑之前进行一些模拟。 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
  • 方法
  • thenReturnwhen 方法返回的OngoingStubbing对象上调用。

然后,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. 创建了一个新的SomeModel mock(在getSomeList()内(,
  4. 模拟方法model.getName()称为,

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

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

这对Mockito来说看起来很愚蠢,因为它无法确定你在用mainModel.getList()做什么。

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

一般来说,像 Mockito 那样依赖静态状态是一个糟糕的设计决策,因为它可能导致违反最小惊讶原则的情况。 然而,Mockito的设计确实使嘲笑变得清晰而富有表现力,即使它有时会导致惊讶。

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

3:如果完成,您将在"thenReturn"指令之前在里面存根另一个模拟的行为

AbcService abcService = mock(AbcService.class);

检查语法:

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

常见错误如下所示:

一个。 doThrow(new RunTimeException()).when(abcService.add(any(), any()))

同样,检查 when((.thenReturn(( 等等。

对于那些使用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的模拟。

GL

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

对于无效方法的模拟,请尝试以下方法:

//Kotlin Syntax
 Mockito.`when`(voidMethodCall())
           .then {
                Unit //Do Nothing
            }
我对想要

分享解决方法的伍德沃德@Luke的详细答案感到非常兴奋。正如伍德沃德@Luke所解释的那样,我们不能有两个电话,比如

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

比可能发生在调用链中。但是,如果您要使用结构:

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

什么时候

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

一切都将按预期工作。

相关内容

  • 没有找到相关文章

最新更新