2011-06-22 60 views
0

對不起,很長的文章...單元測試「結構」的方法?

雖然被引入到棕色領域的項目,我懷疑某些單元測試和思考。假設你有一個repostory類,包裝一個存儲過程並在開發人員指南中包含一些特定的準則(規則),描述應該如何構造這個類。類可能看起來像以下:現在

public class PersonRepository 
{ 
public PersonCollection FindPersonsByNameAndCity(string personName, string cityName) 
{ 
    using (new SomeProfiler("someKey")) 
    { 
     var sp = Ioc.Resolve<IPersonStoredProcedure>(); 

     sp.addNameArguement(personName); 
     sp.addCityArguement(cityName); 

     return sp.invoke(); 
    } 
} } 

,我當然會寫一些集成測試,測試的SP可以被調用,並且該行爲是按預期。然而,我會寫單元測試斷言:

  • 構造用於與所述輸入參數「someKey」 SomeProfiler稱爲
  • PersonStoredProcedure的構造被稱爲
  • 對所存儲的過程中的addNameArgument方法被稱爲與參數PERSONNAME
  • 在存儲過程addCityArgument方法稱爲使用參數的cityName
  • 調用方法被稱爲所存儲的過程 -

如果是這樣,我可能會測試一個方法的整個結構,除了行爲。我最初的想法是,它是矯枉過正。但是,關於團隊實施的編碼實踐,這些測試確保了統一和「正確」的結構,並且下一層被正確調用(從DAL到DB,BLL到DAL等)。

在我的情況下,這些類型的測試是針對應用程序的每一層執行的。

後續問題 - SomeProfiler類的使用有點像傳統給我的感覺 - 而是爲此創建顯式測試,是否可以通過使用靜態代碼分析或unittest + reflection創建約定樣式的測試?

在此先感謝。

回答

0

我認爲你最初的想法是正確的 - 這是一種矯枉過正。儘管您可以使用反射來確保該類具有您期望的方法,但我不確定要如何測試它。

也許應該使用FxCop/StyleCop或nDepend之類的工具來代替單元測試,以確保特定程序集/ dll中的所有類都具有這些屬性。

既然我是「只編寫你需要的代碼」的信徒,爲什麼要測試一個方法的存在,要麼你在代碼中的某個地方使用它,並且你可以測試特定的情況,或者你不需要 - 所以它是無關緊要的。

+0

我會標記爲答案您的回覆。希望對測試商業價值與代碼結構進行更多的討論。但是我對下一個代碼審查有一點點意見。 – jaspernygaard

0

單元測試應該關注行爲,而不是實現。因此編寫一個測試來驗證某些參數是否設置或通過並不會爲您的測試策略增加太多價值。

由於提供的示例似乎與您的數據庫進行通信,因此它不能真正被視爲「單元測試」,因爲它必須與具有其他設置和前提條件的物理依賴項進行通信,例如環境的可用性,數據庫模式,現有數據,存儲過程等。您編寫的任何測試實際上也都驗證這些前提條件。

在目前的情況下,對這些類型的測試最好的辦法是測試類提供的行爲 - 調用存儲庫中的方法,然後驗證結果是否符合您的預期。但是,您會突然意識到這裏存在隱藏成本 - 數據庫在測試運行之間保持狀態,並且您需要額外的設置或拆除邏輯以確保數據庫處於已知狀態。

雖然我意識到這個問題的目的是測試一個「黑匣子」,但看起來很明顯在你的API中有一些隱藏的魔法。解決衆所周知的狀態問題的首選方法是使用內存數據庫,該數據庫的作用範圍是當前的測試,它將我與環境考慮因素隔離,並使我能夠對我的集成測試進行並行化。我敢打賭,在目前的設計下,沒有「接縫」以編程方式引入數據庫配置,因此你「陷入了困境」。以我的經驗,魔法傷害。

然而,現有的設計略有變化解決了這個問題,「神奇」消失:

public class PersonRepository : IPersonRepository 
{ 
     private ConnectionManager _mgr; 

     public PersonRepository(ConnectionManager mgr) 
     { 
     _mgr = mgr; 
     } 

     public PersonCollection FindPersonsByNameAndCity(string personName, string cityName) 
     { 
      using (var p = _mgr.CreateProfiler("somekey")) 
      { 
       var sp = new PersonStoredProcedure(p); 

       sp.addArguement("name", personName); 
       sp.addArguement("city", cityName); 

       return sp.invoke(); 
      } 
     } 
} 
+0

雖然我完全同意你關於解耦代碼和單元集成測試的價值,但是我的意圖並不在於這篇文章,最重要的是測試用例。假設我們使用TypeMock或者StructureMap來解耦依賴關係。我會稍微改變示例代碼... – jaspernygaard