2014-03-25 141 views
0

這不是我的代碼看起來像,但它會接近我想作爲一個例子改變私有方法的行爲進行單元測試

class Phone { 
    public makeCall(String number) { 
     addEntryToCallLog(number) 
     //eventually make a call 
    } 

    private void addEntryToCallLog(String number) { 
     //make database calls 
    } 
} 


class PhoneTest { 
    Phone phone 
    @Test 
    public void canMakeACall() { 
     //mock the behaviour of phone.addEntryToCallLog to prevent database exceptions 
     phone.makeCall("1223"); 
     //Assert somehow that call was made << --IGNORE this NOT IMPORTANT 
    } 
} 

我想在一個單元測試來測試「MakeCall函數」 ,但是當我這樣做時,代碼會拋出一些數據庫異常,從而打破我的測試。 我認爲能夠測試java私有方法的行爲是合理的,因爲這可以爲所有測試提供一致的行爲。

我以前用過groovy,用它可以用spock來模擬私有方法的行爲。我也可以使用metaClass來爲我創建的實例做同樣的事情。但在java中,這似乎並不是一個簡單的方法。

我也試過mockito和power mockito,但它們允許我改變私有方法的返回值,但它們不允許我改變行爲。

這似乎是一個明顯的事情,有人在那裏處理過。我想我錯過了一些東西。但它是什麼。

+0

默認情況下,Mockito模擬調用除了返回指定的值外根本沒有任何行爲。這應該做你想做的事情,因爲不會進行數據庫調用。 –

回答

1

你的例子很奇怪,因爲你的getContactFromDatabase不會返回任何東西,但你用它來設置一個Contact變量。如果您想要模擬出對象的創建行爲,看看這個:

https://code.google.com/p/mockito/wiki/MockingObjectCreation

class PhoneTest { 

    @Spy Phone phone; 

    @Test 
    public void canMakeCall() { 
    doReturn(someContact) 
     .when(phone) 
     .getContactFromDatabase(someString); 

    phone.makeCall(someString); 
    } 
} 

如果你想要做什麼:

class PhoneTest { 

    @Spy Phone phone; 

    @Test 
    public void canMakeCall() { 
    doNothing() 
     .when(phone) 
     .getContactFromDatabase(someString); 

    phone.makeCall(someString); 
    } 
} 

http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#13

你可以創建真實對象的間諜。當你使用間諜,那麼調用真正的方法(除非方法被扼殺)。應該謹慎地偶爾使用真實的間諜 ,例如,在使用遺留代碼處理 時。

+0

我修改了這個問題是明智的。有什麼想法嗎? – user1879106

+0

另外請注意,addEntryToCallLog是私人的 – user1879106

+0

如果你有一個更具體的例子,我可以看看,但你問的是非常抽象的。 'doNothing'應該足夠了,如果你想確保'addEntryToCallLog(number)'被調用,你可以使用'verify(phone).addEntryToCallLog(number)' – DTing

2

我的建議是從private放鬆可視性包裝和私營部門,可能與@VisibleForTesting annotation/** Visible for testing. */音符。此時,您可以使用doAnswer「部分模擬」您的內部方法,如其他答案中提到的DT,以取代實際實現的行爲。

這裏的主要問題是Mockito最擅長將接口轉換爲存根實現。然而,私有方法非常故意不是接口的一部分,因此Java通過一些普通的Mockito依賴的反射功能來使它們變成harder to access。 (在幕後,Mockito正在創建你正在嘲笑的類的一個子類,並且子類通常不能覆蓋私有方法。)

你還應該記住,你的單元測試理想地應該是測試你的類作爲單位,而不需要深入實施細節。通過說你試圖替換你的私人方法的行爲,你就表明你的班級需要一個尚不存在的縫。您可能會考慮重構依賴注入:

class Phone { 
    interface CallLogger { 
    void addEntry(String number); 
    } 

    private final CallLogger logger; 

    public Phone() { 
    this(new DefaultCallLogger()); 
    } 

    /** Visible for testing. */ 
    Phone(CallLogger logger) { 
    this.logger = logger; 
    } 

    /* ... */ 
} 

...然後讓你選擇你想要的任何CallLogger,包括在生產中去數據庫,在測試中僞裝或嘲笑它,或將來用批量版本替換它。對於製作addEntryToCallLog package-private或protected也是如此:您指示子類可以更改它們向調用日誌添加條目的方式,這正是您在測試中要做的事情,並承認這將有助於Java VM和您的代碼的讀者要了解行爲可以改變的地方。


所有這一切說,如果你只是想用PowerMockito替換實現,你可以使用PowerMockito的doAnswerwhen爲您的私有方法提供了不同的行爲。請記住,您有機會改進設計,並花費較少的時間和精力來處理實施細節。

相關問題