我只是不了解 TDD 單元測試(Asp.Net MVC 項目)嗎?
我試圖弄清楚如何正確有效地對我的 Asp.net MVC 項目進行單元測試。當我開始這個項目時,我購買了 Pro ASP.Net MVC,並通過那本書了解了 TDD 和單元測試。看到這些範例,以及我在目前公司擔任 QA 軟體工程師的事實後,我對 TDD 看起來多麼棒感到驚訝。所以我開始著手我的項目,並開始為我的數據庫層、業務層和控制器編寫單元測試。一切都在實施之前進行了單元測試。起初我認為這很棒,但後來事情開始走下坡路。
以下是我開始遇到的問題:
- 我最終編寫了應用程式碼,以便能夠執行單元測試。我的意思不是很好,因為我的程式碼被破壞了,我必須修復它才能通過單元測試。我的意思是,由於使用 linq 進行數據檢索(使用通用儲存庫模式),將數據庫抽象為模擬數據庫是不可能的。
原因是使用 linq->sql 或 linq->entities 只需執行以下操作即可進行連接:
var objs = select p from _container.Projects select p.Objects;但是,如果您模擬數據庫層,為了讓該 linq 通過單元測試,您必須將 linq 更改為
var objs = select p from _container.Projects join o in _container.Objects on o.ProjectId equals p.Id select o;這不僅意味著您正在更改您的應用程序邏輯,以便您可以對其進行單元測試,而且您正在降低您的程式碼效率,僅出於可測試性的目的,並且首先擺脫了使用 ORM 的許多優勢.
此外,由於我的模型的許多 ID 都是由數據庫生成的,因此我證明必須編寫額外的程式碼來處理非數據庫測試,因為從未生成過 ID,而且我仍然必須處理這些情況才能通過單元測試,但它們永遠不會出現在真實場景中。
因此,我最終放棄了我的數據庫單元測試。
- 只要我返回視圖,為控制器編寫單元測試就很容易。然而,我的應用程序的主要部分(也是從單元測試中受益最多的部分)是一個複雜的 ajax Web 應用程序。出於各種原因,我決定將應用程序從返回視圖更改為返回包含所需數據的 JSON。發生這種情況後,我的單元測試編寫起來非常痛苦,因為我還沒有找到任何為非平凡的 json 編寫單元測試的好方法。
在敲了敲頭並浪費了大量時間試圖找到一種對 JSON 進行單元測試的好方法之後,我放棄並刪除了所有控制器單元測試(到目前為止,所有控制器操作都集中在應用程序的這一部分)。
- 所以最後我只剩下測試服務層(BLL)了。現在我正在使用 EF4,但是我也遇到了 linq->sql 的問題。我選擇使用 EF4 模型優先方法,因為對我來說,這樣做很有意義(定義我的業務對象並讓框架弄清楚如何將其轉換為 sql 後端)。一開始還好,但現在因為關係變得很麻煩。
例如說我有
Project,User, 和Object實體。一個對象必須與一個項目相關聯,而一個項目必須與一個使用者相關聯。這不僅是特定於數據庫的規則,這些也是我的業務規則。但是,假設我想做一個能夠保存對象的單元測試(舉個簡單的例子)。我現在必須執行以下程式碼以確保保存有效:User usr = new User { Name = "Me" }; _userService.SaveUser(usr); Project prj = new Project { Name = "Test Project", Owner = usr }; _projectService.SaveProject(prj); Object obj = new Object { Name = "Test Object" }; _objectService.SaveObject(obj); // Perform verifications僅僅為了執行一個單元測試就必須做這一切有很多問題。這有幾個問題。
- 對於初學者,如果我添加一個新的依賴項,例如所有項目必須屬於一個類別,我必須進入每個引用項目的單個單元測試,添加程式碼以保存類別,然後添加程式碼以將類別添加到項目中。對於一個非常簡單的業務邏輯更改來說,這可能是一個巨大的努力,但是我將為這個需求修改的幾乎所有單元測試實際上都不是為了測試那個特性/需求。
- 如果我隨後向我的 SaveProject 方法添加驗證,以便項目無法保存,除非它們的名稱至少包含 5 個字元,然後我必須通過每個對象和項目單元測試以確保新要求不會任何不相關的單元測試都會失敗。
- 如果方法中存在問題,
UserService.SaveUser()它將導致所有項目和對象單元測試失敗,並且無需深入研究異常,原因就不會立即被發現。因此,我已經從我的項目中刪除了所有服務層單元測試。
我可以繼續下去,但到目前為止,我還沒有看到任何單元測試真正幫助我而不妨礙我的方法。我可以看到我可以並且可能會實施單元測試的特定案例,例如確保我的數據驗證方法正常工作,但這些案例很少而且相差甚遠。我的一些問題可能會得到緩解,但如果不向我的應用程序添加額外的層就無法緩解,因此會產生更多的故障點,以便我可以進行單元測試。
因此,我的程式碼中沒有單元測試。幸運的是,我大量使用原始碼控制,因此如果需要,我可以將它們取回,但我只是不明白這一點。
在網際網路上,我到處都能看到人們談論 TDD 單元測試有多棒,而我不僅僅是在談論狂熱的人。少數拒絕 TDD/單元測試的人給出了不好的論據,聲稱他們通過 IDE 手動調試更有效,或者他們的編碼技能非常棒以至於他們不需要它。我承認這兩個論點都是徹頭徹尾的廢話,尤其是對於需要由多個開發人員維護的項目,但任何對 TDD 的有效反駁似乎很少而且相差甚遠。
所以這篇文章的重點是問,我只是不了解如何使用 TDD 和自動單元測試嗎?
您可以查看我編寫的範例 ASP.NET MVC 2.0 項目結構。它提供了一些概念,可以幫助您開始對控制器邏輯和數據庫進行單元測試。就數據庫測試而言,它不再是單元測試,而是集成測試。正如您將在我的範例中看到的,我正在使用 NHibernate,它允許我輕鬆切換到為每個測試夾具重新創建的範例 SQLite 數據庫。
最後,在 ASP.NET MVC 中進行單元測試可能會很痛苦,如果沒有適當分離關注點和抽象,並且使用模擬框架和MVCContrib.TestHelper等框架可以讓您的生活更輕鬆。
這是您的控制器單元測試的外觀預覽。
更新:
作為對評論的回應,我認為程式是一項具體的任務,對於如何對複雜的業務應用程序進行單元測試,很難給出最終答案。為了能夠測試複雜的應用程序,層之間的耦合應該盡可能弱,這可以通過介面和抽像類來實現。儘管我同意在復雜的應用程序中實現如此弱的耦合併不是一件容易的事。
我可以給你一個建議:如果整個 TDD 概念很難理解並且你看不到它的好處,那麼這沒關係。沒有人能證明 TDD 在所有情況下都是有益的。試著設計你的應用程序,讓每個類都有一個單一的職責。如果你發現自己在同一個類中驗證輸入、SQL 數據訪問和異常處理,那麼你做錯了。一旦實現了這種分離,您將看到單元測試變得更加容易,您甚至可以進入單元測試將推動您的開發的階段:-)
就單元測試而言
JsonResult,MvcContrib.TestHelper這是一個具體的問題,我給出了一個具體的答案:public class MyModel { public string MyProperty { get; set; } } public class HomeController : Controller { public ActionResult Index() { return Json(new MyModel { MyProperty = "value" }); } }和測試:
[TestMethod] public void HomeController_Index_Action_Should_Return_Json_Representation_Of_MyModel() { // arrange var sut = new HomeController(); // act var actual = sut.Index(); // assert actual .AssertResultIs<JsonResult>() .Data .ShouldBe<MyModel>("") .MyProperty .ShouldBe("value"); }