2010-02-26 77 views
6

我正在開始一個新項目(當然,重新啓動一個現有的項目),並嘗試採用TDD(第n次)來獲得它應該帶來的所有好處。TDD'ing MVC控制器驅動設計

我相信TDD會導致我的測試驅動我只寫我需要寫的代碼,但它會促使我編寫我需要的代碼,而不會留下一些代碼。

這是我目前不確定性的狀態進來

考慮故事:

「的用戶必須能夠添加窗口小部件,這樣做,他們被帶到查看的新細節添加小部件「。

好吧,所以在UI上工作(因爲這是用戶將從中添加它們的小部件,而不是使用Visual Studio和我編寫的一組程序)...我從以下測試開始,編寫最小化,以便測試通過。

所以我開始與控制器拋出一個NotImplementedException,然後返回一個View()...以下是我寫了最少的線,我可以使測試通過第一點。

[TestFixture] 
public class WidgetControllerTester 
{ 

    [Test] 
    public void Create_IfBusinessModelIsValid_ReturnRedirectToRouteResultToDetailsAction() 
    { 
    // Arrange 
     var currentUser = new User 
           { 
            DisplayName = "Fred", 
            Email = "[email protected]", 
            Password = "pass", 
            Status = UserStatus.Active 
           }; 
     var model = new WidgetModel(); 
     var controller = new WidgetController(); 
     // Act 
     var actionResult = controller.Create(currentUser, model); 
     // Assert 
     actionResult.AssertActionRedirect().ToAction("Details"); 
    } 
} 

public class WidgetModel 
{ 
} 

public class WidgetController: Controller 
{ 
    public ActionResult Create() 
    { 
     return View("Create"); 
    } 

    [HttpPost] 
    public ActionResult Create(User currentUser, Widget model) 
    { 
     return RedirectToAction("Details"); 
    } 
} 

現在我意識到無效模型和模型狀態檢查的附加測試將從其他故事演變而來。

但是我看不到一條清晰的路徑,我將如何利用附加測試來驅動控制器內的其他代碼。

例如,我知道在某些時候我會想要從Create操作進行WidgetService調用。我錯過了一些明顯的東西(不能夠看到樹木的東西)我如何通過額外的測試來實現控制器代碼?

談到WidgetService,我期望我會寫一個WidgetServiceTester,並且暫時引用控制器內的內容很可能會被嘲笑。

一些想法我有...

  • 創建一個新的測試稱爲Create_IfModelIsValid_WidgetIsAddedToRepository,但如何這顯然導致到控制器中的行動服務電話?
  • 我需要寫一個更詳細的故事,說明該模型需要插入到存儲庫/數據庫等?
  • 我是否混淆了TDD和XP的元素?

感謝您的閱讀,我將不勝感激任何反饋意見和見解的最佳實踐進展。

喬。

編輯2010年2月27日

我發現下面的文章迭代#6 - 使用測試驅動開發(上asp.net)(http://www.asp.net/%28S%28ywiyuluxr3qb2dfva1z5lgeg%29%29/learn/mvc/tutorial-31-cs.aspx),這表明了那種我以後的事情,但是他們似乎考慮將控制庫/服務添加到控制器中作爲重新考慮......我個人不同意,我錯了嗎?:)

我打算考慮編寫一個檢查Details行爲的ViewData並更新這個問題的測試。

回答

1

喬。有時候我也會有同樣的不確定性。但我也認爲你錯過的大部分內容都是那些前期的故事。首先,你應該分解你的故事只是一點點,以創建實際的開發人員要求。這就是我的意思:

「用戶必須能夠添加一個小部件,這樣他們才能查看新添加的小部件的細節。」

好了,所以對我來說,爆發的問題如下,它可以幫助你思考的測試:

「用戶」 - 哪裏的用戶來自哪裏?他們如何登錄? (如果你使用默認的AccountController和測試,那麼這已經存在,如果沒有,你需要測試獲得登錄表單,登錄,驗證成功和失敗登錄等)

「add一個小部件「 - 到什麼(我不是指實現,我只是指出,這意味着你要麼打一個存儲庫或一個服務,除非'添加'只是意味着將其添加到正在運行的實例和你不需要持久性)?該部件在此時必須是有效的,還是可以保存無效的widgets並在以後生效?這意味着我測試一個存儲庫或服務有一個方法命中(save(),insert(),add(),任何(不是方法的內部,直到你測試你的服務/ repo,只是控制器通過調用它的工作來完成它的工作),檢查在有效/無效的小部件上發生了什麼(您需要擴展一下故事或添加故事以涵蓋在有效/無效小部件上應該發生的情況)

「用於查看新添加的窗口小部件的詳細信息「 - 稍微重寫,但基本上是你說的。總是?還是隻有成功?此視圖是可編輯還是隻讀(即編輯操作或細節操作)?是否有消息用戶告訴他們他們已經成功了,或者他們是否應該從他們正在查看他們的窗口小部件中推斷他們是成功的這一事實推斷出來的呢?這應該推動測試在返回的actionresult和che上執行諸如檢查屬性ck存儲在TempData(狀態消息)中的值以及檢查兩個路徑中發生了什麼(成功或失敗)。

這只是一個快速的拍攝,但基本上這就是思考過程。你也可以做同樣的W /其他故事,併爲此產生新的故事,以涵蓋更多的應用程序行爲。

對你的設計前進幾點想法。

您的下一個測試應該看看我上面寫的是什麼,這是控制器創建POST動作應該1)接收所需數據(您的參數),2)調用該服務/存儲庫以「添加」 ,3)如果添加失敗,可能會做一些事情(這是在你的設計中;我已經到了我的控制器認爲一切都會好的地方,並且我通過屬性處理失敗,但這是個人設計決定),4)重定向到細節。因此,你的下一個測試將使用模擬(我更喜歡谷歌代碼上的moq庫,但無論你有什麼工作)。您將需要一些界面來描述您的控制器將調用的服務,並且您將模擬的實現傳遞給您的控制器,以確保它正在調用正確的方法。在起訂量,這會是這個樣子:

[Test] 
public void Create_CallsRepository() 
{ 
    // Arrange 
    var currentUser = new User 
          { 
           DisplayName = "Fred", 
           Email = "[email protected]", 
           Password = "pass", 
           Status = UserStatus.Active 
          }; 
    var model = new WidgetModel(); 
    var mockService = new Mock<IService<WidgetModel>(); 
    mockService.Setup(s=>s.Add(model)); //.Returns(whatever) if it returns something 
    var controller = new WidgetController(mockService.Object); 

    // Act 
    var actionResult = controller.Create(currentUser, model); 

    // Assert 
    mockService.Verify(s=>s.Add(model)); 
} 

這使得一些設計假設,當然,而是如何寫你的測試VS你的對象應該如何調用/處理事情的張力的部分是什麼使TDD如此寶貴。