2009-07-02 56 views
5

所以我有一個方法的類,如下所示:我如何單元測試的實現細節比如緩存

public class SomeClass 
{ 
    ... 

    private SomeDependency m_dependency; 

    public int DoStuff() 
    { 
     int result = 0; 
     ... 
     int someValue = m_dependency.GrabValue(); 
     ... 
     return result; 
    } 
} 

而且我已經決定,而不是調用m_dependency.GrabValue()每一次,我真的想緩存內存中的值(即,在這個類中),因爲我們每次都會得到相同的值(依賴關係從數據表中獲取並從幾乎不會改變的表中獲取)。

我遇到了問題,但試圖在單元測試中描述這種新行爲。我嘗試了以下操作(我正在使用NUnit和RhinoMocks):

[Test] 
public void CacheThatValue() 
{ 
    var depend = MockRepository.GeneraMock<SomeDependency>(); 

    depend.Expect(d => d.GrabValue()).Repeat.Once().Return(1); 

    var sut = new SomeCLass(depend); 
    int result = sut.DoStuff(); 
    result = sut.DoStuff(); 
    depend.VerifyAllExpectations(); 
} 

但是這並不起作用;即使沒有引入對功能的任何改變,該測試也會通過。我究竟做錯了什麼?

+2

不好意思問這個問題,但爲什麼要測試某個實現細節呢? – Robert 2009-07-02 03:38:33

回答

5

我看到緩存與正在做的東西是正交的。我會找到一種方法來將緩存邏輯拉到方法之外,或者通過改變SomeDependency或者以某種方式包裝它(我現在對基於lambda表達式的緩存類 - yum有個很好的想法)。

這樣你的DoStuff測試不需要改變,你只需要確保它們與新的包裝器一起工作。然後,您可以獨立地測試SomeDependency或其包裝器的緩存功能。使用架構良好的代碼將緩存層放在適當的位置應該相當容易,您的依賴或實施都不應該知道其差異。

單元測試不應該測試實現,他們應該測試行爲。同時,被測試者應該有一組狹義的行爲。

要回答您的問題,您正在使用動態模擬,並且默認行爲是允許任何未配置的調用。額外的呼叫只是返回「0」。您需要設置一個期望,即不再對其進行相關性調用:

depend.Expect(d => d.GrabValue()).Repeat.Once().Return(1); 
depend.Expect(d => d.GrabValue()).Repeat.Never(); 

您可能需要進入記錄/重播模式以使其正常工作。

+0

好處:「單元測試不應該測試實現,他們應該測試行爲,同時,被測試者應該有一組狹義的行爲。」這回答了我的問題(羅伯特對我的問題的評論也是如此) – jpoh 2009-07-02 03:54:50

4

這似乎是「測試驅動設計」的情況。如果緩存是SubDependency的實現細節 - 因此不能直接測試 - 那麼可能需要公開其某些功能(特別是其緩存行爲) - 因爲在SubDependency中公開它並不自然,所以需要暴露在另一個類中(我們稱之爲「緩存」)。當然,在Cache中,行爲是契約性的 - 公開的,因此是可測試的。

所以測試 - 和氣味 - 告訴我們我們需要一個新班級。測試驅動設計。這不是很好嗎?