2011-06-15 62 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将始终报告该模型即使不是有效也是有效的。