2011-06-15 61 views
1

我對控制器發佈了操作。代碼如下TryUpdateModel從單元測試用例中導致錯誤(Asp.net mvc)

 [HttpPost] 
    public ActionResult Create(Int64 id, FormCollection collection) 
    { 
     var data = Helper.CreateEmptyApplicationsModel(); 

     if (TryUpdateModel(data)) 
     { 
       // TODO: Save data 
      return RedirectToAction("Edit", "Applications", new { area = "Applications", id = id }); 
     } 
     else 
     { 
      // TODO: update of the model has failed, look at the error and pass back to the view 
      if (!ModelState.IsValid) 
      { 
       if (id != 0) Helper.ShowLeftColumn(data, id); 
       return View("Create", data); 
      } 
     } 

     return RedirectToAction("Details", "Info", new { area = "Deals", InfoId= id }); 

    } 

我寫測試用例這個如下

[TestMethod] 
    public void CreateTest_for_post_data() 
    {   
     var collection = GetApplicantDataOnly();   
     _controller.ValueProvider = collection.ToValueProvider(); 
     var actual = _controller.Create(0, collection); 
     Assert.IsInstanceOfType(actual, typeof(RedirectToRouteResult)); 
    } 

給我調試這個單一測試用例,測試情況下通過,因爲條件 如果(TryUpdateModel(數據))返回true,如果條件成立,則返回true。 但是,當我從整個解決方案調試測試用例時,此測試用例失敗,因爲它遇到「if(TryUpdateModel(data))」。

我不知道爲什麼..

請幫助...

感謝

回答

0

調試你的測試和檢查的ModelState錯誤集合,所有的錯誤所遇到的tryupdatemodel應該在那裏。

1

您可能要清理你的代碼位:

[HttpPost] 
public ActionResult Create(int id, FormCollection collection) 
{ 
    var data = Helper.CreateEmptyApplicationsModel(); 

    if (!ModelState.IsValid) 
    { 
     if (id != 0) 
     { 
      Helper.ShowLeftColumn(data, id); 
     } 

     return View("Create", data); 
    } 

    if (TryUpdateModel(data)) 
    { 
     return RedirectToAction("Edit", "Applications", new { area = "Applications", id = id }); 
    } 

    return RedirectToAction("Details", "Info", new { area = "Deals", InfoId= id }); 
} 

不要使用Int64,只需使用int

至於你失敗的測試,我希望你的測試一直失敗,因爲TryUpdateModel將返回false。在單元測試中運行代碼時,控制器的控制器上下文不可用,因此TryUpdateModel將失敗。

您需要以某種方式假冒/模擬TryUpdateModel,以便它實際上不會正常運行。相反,你「假」它返回true。下面是一些鏈接,可以幫助:

How do I Unit Test Actions without Mocking that use UpdateModel?

以上SO答案顯示了使用RhinoMocks這是一個免費的模擬框架的例子。

或者這樣:

http://www.codecapers.com/post/ASPNET-MVC-Unit-Testing-UpdateModel-and-TryUpdateModel.aspx

1

我所經歷的,這將解決您的問題,只要你有類似的問題不需要使用FormCollection

自從我學習了自動裝訂功能後,我還沒有使用過TryUpdateModel。自動綁定,幾乎沒有工作TryUpdateModel會爲你做,也就是說,它會根據FormCollection中的值設置一個模型對象,並嘗試驗證模型。它會自動完成。你所要做的就是在ActionMethod中放置一個參數,它會自動讓它的屬性充滿FormCollection中的值。然後,您的操作簽名會變成這樣:

public ActionResult Create(Int64 id, SomeModel data) 

現在你不需要調用TryUpdateModel可言。您仍然需要檢查ModelState是否有效,以決定是否重定向或返回視圖。

[HttpPost] 
public ActionResult Create(Int64 id, SomeModel data) 
{ 
    if (ModelState.IsValid) 
    { 
      // TODO: Save data 
     return RedirectToAction("Edit", "Applications", new { area = "Applications", id = id }); 
    } 
    else 
    { 
     if (id != 0) Helper.ShowLeftColumn(data, id); 
     return View("Create", data); 
    } 
} 

這不會在您的單元測試中拋出異常,這樣就解決了一個問題。但是,現在還有另一個問題。如果你使用上面的代碼運行你的應用程序,它會工作得很好。您的模型將在輸入Action後進行驗證,並且將返回正確的ActionResult(重定向或視圖)。但是,當您嘗試單元測試兩條路徑時,即使模型無效,您也會發現該模型始終會返回重定向。

問題是,當單元測試時,模型根本沒有被驗證。由於ModelState默認有效,所以ModelState.IsValid將在您的單元測試中始終返回true,因此即使在模型無效時也會始終返回重定向。

解決方案:致電TryValidateModel而不是ModelState.IsValid。這將強制你的單元測試來驗證模型。這種方法的一個問題是,這意味着模型將在單元測試中驗證一次,在應用程序中驗證兩次。這意味着任何發現的錯誤都會在應用程序中記錄兩次。這意味着如果您在視圖中使用ValidationSummary輔助方法,則會看到列出的一些重複消息。

如果這太難了,可以在致電TryValidateModel之前清除ModelState。這樣做存在一些問題,因爲您將丟失一些有用的數據,例如,如果您清除ModelState,則嘗試的值可以清除ModelState中記錄的錯誤。您可以用深挖的ModelState和清除存儲在每一個項目,像這樣每一個錯誤這樣做:

protected void ClearModelStateErrors() 
{ 
    foreach (var modelState in ModelState.Values) 
     modelState.Errors.Clear(); 
} 

我放在代碼的方法,因此可以被所有操作可以重複使用。我還添加了protected關鍵字來暗示這可能是放置在BaseController中的一種有用方法,即所有控制器派生自它們以便它們都可以訪問此方法。

最終解決方案:

注:我知道我沒有掉根發出任何光線。這是因爲我不完全瞭解根本問題。如果您注意到失敗的單元測試,則會失敗,因爲ArgumentNullException因爲ControllerContext爲空而被拋出,並且它被傳遞給拋出異常(如果ControllerContext爲空)的方法。 (用他們該死的防守編程詛咒MVC團隊)。

如果您試圖模擬ControllerContext,您仍然會遇到異常,這次是NullReferenceException。有趣的是,異常的堆棧跟蹤顯示這兩個異常都以相同的方法出現,或者我應該說構造函數,位於System.Web.Mvc.ChildActionValueProvider。我沒有方便的源代碼副本,所以我不知道是什麼導致異常,我還沒有找到比上面提供的更好的解決方案。我個人不喜歡我的解決方案,因爲我正在改變編寫我的應用程序以利於單元測試的方式,但似乎沒有更好的選擇。我敢打賭,真正的解決方案將涉及嘲笑一些其他的對象,但我不知道是什麼。

而且,任何人得到任何奇思妙想之前,嘲諷ValueProvider是的解決方案。它會阻止例外情況,但是您的單元測試不會驗證您的模型,並且您的ModelState將始終報告該模型即使不是有效也是有效的。