2011-04-20 186 views
3

假設我們有方法在類A中測試,從類B調用方法。爲了測試它,我們爲B創建了模擬,然後驗證它是否被調用。驗證(...)足夠進行單元測試還是需要聲明測試方法的實際結果? 以下是簡化例子來闡明我的擔憂:Mockito中模擬方法的單元測試最佳實踐

public class StringWriterATest { 
    StringWriterB b = mock(StringWriterB.class); 

    @Test 
    public void stringWriterATest() { 
     StringBuffer sb = new StringBuffer(); 
     StringWriterA a = new StringWriterA(); 
     a.stringWriterB=b; 

     a.append(sb); 

     ArgumentCaptor<StringBuffer> argument = ArgumentCaptor.forClass(StringBuffer.class); 
     verify(b).append(argument.capture()); 

     assertEquals("StringWriterA", ((StringBuffer)argument.getValue()).toString()); 

     //do we really need this or above is enough for proper unit test of method a.append(sb); 
     //assertEquals("StringWriterA_StringWriterB", sb); 
    } 
} 


public class StringWriterA { 
    public StringWriterB stringWriterB; 

    public void append(StringBuffer sb) { 
     sb.append("StringWriterA"); 
     stringWriterB.append(sb); 
    } 
} 

class StringWriterB { 
    public void append(StringBuffer sb) { 
     sb.append("StringWriterB"); 
    } 
} 

問候, 最大

回答

0

在你的榜樣,StringWriterB店無狀態和append方法可以很容易地是靜態的。在這種情況下,該呼叫純屬副作用,無需進行測試。

但是,我懷疑你的真實代碼要複雜得多。如果存在訪問StringWriterB的另一個對象,那麼您可能希望嘲笑它,以防意外調用它。如果您希望將來可以擴展B,那麼您可能還想添加B的驗證 - 可能會從append調用中存儲狀態並使用訪問器。

有一點要考慮的是呼叫的目的StringWriterA.append()是。如果它的工作是追加字符串StringWriterAStringWriterB那麼這就是你應該測試和模擬是沒有必要的。 StringWriterA如何完成該任務並不重要。但是,如果它的一部分工作是調用StringWriterB.append()方法,那麼除非要在A的測試中測試StringWriterB,否則可能需要進行模擬。

我的經驗法則WRT嘲笑是使用真實的物體,直到我沒有直接測試的物體的佈線變得太毛或太脆。如果我覺得我的測試很大一部分是在實際測試其他物體,那麼嘲笑將是一個好主意。

+0

感謝您的回答灰色,是的這個例子只是試圖瞭解如何測試與其他模擬交互a.append(sb)。我剛剛更新了它,使我的問題更加清晰。我試圖瞭解,如果從示例驗證足以正確測試A類的追加方法 – maxx 2011-04-20 01:28:06

1

從不需要模擬返回值並同時驗證對象。

考慮一下:

StringWriterA正在測試的類。因此你一定要使用斷言來驗證這個類的行爲。爲了做到這一點,你嘲笑一個依賴,StringWriterB

你做希望在您的StringWriterA測試,測試StringWriterB,因此在測試中StringWriterB相互作用的任何斷言都錯了地方

您必須假定StringWriterB的行爲與預期的相同。您要驗證StringWriterA調用StringWriterB正確(使用verify()您想要模擬其預期行爲並模擬返回值。

如果你模擬,那麼驗證是隱含的,因爲如果方法未被調用,模擬的返回值將不會被返回。

在你的情況下,StringWriterA.append()不會返回任何值,所以只有驗證是可能的。 StringWriterB.append()也可以在其自己的stringWriterBTest中進行類似的驗證測試。

注意:很明顯的測試。由於測試方法永遠不會在框架之外調用,因此永遠不需要輸入它們,因此與生產代碼方法相比,您可以擁有更長的方法名稱。一個很好的約定是:

<underTest>Should<Expected>[When]<Condition>()

stringWriterAShouldAppendConstantAndDelegateToStringWriterB() stringWriterAShouldThrowNullPointerExceptionWhenNullArgument()

當你在你的構建(持續集成)測試失敗,那麼你不必去追捕了什麼錯誤的,方法名稱顯示正確的失敗,您可以閱讀它確切知道必須修復哪些行爲。

0

首先試圖向某人展示你寫的測試。我的看法很難閱讀。 你無法從中判斷你正在測試的是什麼行爲。一個簡短的介紹給你如何使它更具可讀性可以在這裏找到How to Write Clean, Testable Code。 使用參數捕獲器也是一種氣味。一些例子如何避免它可以在這tdd blog找到。

爲了回答您的問題verify用於驗證交互類之間它用於驅動代碼的設計。結果(如果需要)應由whengiven指定開始您的測試。

更多信息如何與嘲笑(whengivenverify,...),以及如何嘲笑是不同的,以樁駕駛您的設計可以在這裏找到:Mocks are not stubs。這個例子使用JMock而不是Mockito來定義mock,但它非常相似(它是關於概念的,而不是實現的細節和你使用的庫)。