簡體   English   中英

在 Mockito 中檢測到未完成的存根

[英]Unfinished Stubbing Detected in Mockito

運行測試時出現以下異常。 我正在為 mocking 使用 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;
    }
}

你在嘲諷中嵌套嘲諷。 在完成MyMainModel之前,您正在調用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 看不到你的代碼,你也可以這樣寫你的 mocking:

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()被調用,

在這一點上,Mockito 感到困惑。 它以為你在mainModel.getList() ,但現在你告訴它你想模擬model.getName()方法。 對於 Mockito,您似乎正在執行以下操作:

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

這對Mockito來說看起來很傻,因為它不能確定你在用mainModel.getList()做什么。

請注意,我們沒有進入thenReturn方法調用,因為 JVM 需要先評估此方法的參數,然后才能調用該方法。 在這種情況下,這意味着調用getSomeList()方法。

通常,依賴靜態狀態是一個糟糕的設計決策,就像 Mockito 所做的那樣,因為它可能導致違反最小驚訝原則的情況。 然而,Mockito 的設計確實提供了清晰而富有表現力的嘲諷,即使它有時會讓人感到驚訝。

最后,最近版本的 Mockito 在上面的錯誤消息中添加了額外的一行。 此額外行表示您可能與此問題處於相同的情況:

3:如果完成,您正在'thenReturn'指令之前存根內部另一個模擬的行為

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.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

請閱讀這篇文章,它有特殊的解釋和工作方法。 該問題的另一個可能原因和解決它的解決方案是將實際模擬移動到測試的早期階段。

AbcService abcService = mock(AbcService.class);<\/code>

檢查語法:<\/strong><\/em>

  1. 常見錯誤如下所示:<\/strong>

    doThrow(new RunTimeException()).when(abcService.add(any(), any()))<\/code>

    同樣,檢查 when().thenReturn() 等等。

我對@Luke Woodward 想要分享解決方法的詳細回答感到非常興奮。 正如@Luke Woodward 解釋的那樣,我們不能有兩個電話

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM