2011-10-02 76 views
3

類似的話題已經在The value of high level unit tests and mock objects單元測試是在高的抽象層次

但是討論的方法,我想描述一個具體情況,問你對我應該怎麼寫單元測試的意見。

我正在開發一個普通的3層應用程序,它使用實體框架。上述EF,我有兩個層次:

  • :他們直接訪問EF ObjectContext的和做所有的CRUD工作(實際上,這些類與T4模板生成)。所有Repository類都有一個適當的接口。
  • 經理:他們實現更高級別的業務邏輯,他們不直接訪問ObjectContext,而是使用合適的Repository。管理員不知道具體的Repository-implementation,只有界面(我使用依賴注入,並在單元測試中模擬)。

沒有進一步的說明,這裏是我想編寫單元測試的類:

public class PersonManager 
{ 
    private IPersonRepository personRepository; // This is injected. 

    // Constructor for injection is here. 

    public void ComplexMethod() 
    { 
     // High level business logic 
     bool result = this.SimpleMethod1(); 
     if(result) 
      this.SimpleMethod2(1); 
     else 
      this.SimpleMethod2(2); 
    } 

    public bool SimpleMethod1() 
    { 
     // Doing some low-level work with the repository. 
    } 

    public void SimpleMethod2(int param) 
    { 
     // Doing some low-level work with the repository. 
    } 
} 

它是用的一個模擬實例化PersonManager很容易單元測試SimpleMethod1SimpleMethod2PersonRepository

但我找不到任何方便的方法來單元測試ComplexMethod

你有什麼建議我應該怎麼做?或者不應該單元測試?也許我不應該使用this參考ComplexMethod中的方法調用,而是通過一個接口訪問PersonManager本身,並用一個模擬代替它。

在此先感謝您的任何建議。

+1

爲什麼你不能測試ComplexMethod?你可能不需要爲SimpleMethod 1&2測試設置額外的期望... –

回答

5

紀堯姆的回答很好(+1),但我想提供一個額外的觀察。我在你發佈的代碼中看到的是一個很普遍的問題的基礎,這是人們試圖找出(或反駁)TDD的一個常見問題,它是:

「我/我爲什麼要測試ComplexMethod(),因爲它取決於SimpleMethod1()和SimpleMethod2(),它們已經過測試,並且具有它們自己的行爲,我必須在ComplexMethod()的測試中考慮它們嗎?我必須基本上覆制SimpleMethod1()和SimpleMethod2 ()爲了完全測試ComplexMethod(),這只是愚蠢的。「

不久之後,他們通常會發現部分嘲諷。使用部分模擬,你可以模擬SimpleMethod1()和SimpleMethod2(),然後使用普通模擬機制測試ComplexMethod()。 「聽起來很棒,」他們認爲,「這將完美解決我的問題!」。但是,一個好的模擬框架應該強烈地阻止以這種方式使用部分模擬,因爲現實是:

您的測試告訴你一個設計問題。

具體來說,他們告訴你,你在一個類中混合了關注點和/或抽象層次。他們告訴你SimpleMethod1()和SimpleMethod2()應該被提取到這個類所依賴的另一個類。無論我看到這種情況多少次,無論開發人員爭論多麼激烈,測試在100%的時間內都被證明是正確的。

+0

+1聽測試! – TrueWill

+1

完全同意!我甚至會補充說,如果他開始編寫測試(TDD或BDD,如果使用模擬),他很可能不會因爲這個設計問題而結束,但是這兩個類會從測試中出現「有機」 – Guillaume

+0

謝謝對於這個答案,這很有道理。然而,在此之前,我沒有發現在同一個類中具有不同抽象層次上的方法的設計問題。我的意思是,在我的應用程序中爲每個抽象級別創建一個新類時感覺有點模糊。在上面的例子中,我處於一種特定的情況,因爲我加入了一個長期運行的項目,之前沒有任何單元測試,我應該剛開始編寫測試......但是在下一個「新鮮」項目我一定會嘗試遵循你的建議,謝謝! –

3

我沒有看到問題所在。您可以在嘲笑存儲庫時測試複雜的方法,這沒有問題。你會需要兩個單元測試,每個測試都會使用與SimpleMethod1測試中相同的期望和執行順序(我假設你已經有了兩個針對SimpleMethod1的單元測試,一個用於返回「true」,一個用於「false」),以及您對SimpleMethod2測試的相同期望,分別使用固定參數1或2。

當然,你的測試課會有一些「重複」,但這不是問題。

另請注意,您對SimpleMethod2的測試不應該對傳遞的參數做任何假設:在「真實生活」中,您可以只有1或2作爲參數(這就是您對ComplexMethod的單元測試所具有的),但是對於SImpleMethod2的單元測試應該測試它的任何參數:any int。

最後,如果ComplexMethod是調用SimpleMethod1和/或SimpleMethod2的唯一方法,則應該考慮將它們設置爲private,並且只對ComplexMethod進行單元測試。

這有道理嗎?

+0

感謝您的答案。事實上,這不是真正的生活的例子,只是我畫的一個簡單的例子。在實際的應用程序中,ComplexMethod要複雜得多,並且調用10個或更多更簡單的類的方法,並且可以有兩個以上的可能執行路徑。因此,測試所有執行路徑並測試更簡單方法的功能幾乎是不可能的。我想我將嘗試將'this'引用改爲一些靜態調用,我可以在測試中用模擬替換。 –