2014-10-11 39 views
66

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

哪條線是355? – Mureinik 2014-10-11 19:44:37

+0

嗨Mureinik,我已更新行號 – 2014-10-11 20:34:41

回答

177

你在嵌套嘲笑裏面嘲笑。在完成MyMainModel的嘲弄之前,你打電話給getSomeList(),這有些嘲弄。當你這樣做時,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返回的ThOD。

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版本在上面的錯誤消息中增加了一行。這額外的行表示您可能會在相同的情況下這個問題:

3:你是磕碰之前「thenReturn」指令內的另一個模擬的行爲,如果完成

+0

感謝盧克。這工作 – 2014-10-11 21:53:06

+0

有沒有這個事實的任何解釋?解決方案可行我不明白爲什麼'就地'的模擬創作不起作用。當你創建模擬並通過引用其他模擬傳遞創建的模擬時,它可以工作。 – Sergey 2014-11-29 15:30:31

+0

感謝您的更新 – Sergey 2014-11-30 10:47:16