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(data))返回true,则转到if条件。 但是,当我从整个解决方案中调试测试用例时,该测试用例失败了,因为它进入了其他条件\“ if(TryUpdateModel(data))\”。 我不知道为什么。 请帮忙... 谢谢     

解决方法

        我遇到了类似的问题,只要您不需要使用
FormCollection
,它就会解决您的问题。 自从了解自动绑定功能的那一天以来,我从未使用过ѭ3。简而言之,自动绑定工作ѭ3可以为您完成很多工作,也就是说,它将根据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);
    }
}
这不会在您的单元测试中引发异常,因此这是一个已解决的问题。但是,现在还有另一个问题。如果您使用上述代码运行应用程序,则可以正常运行。输入操作后,将验证您的模型,并返回正确的ActionResult(重定向或视图)。但是,当您尝试对两个路径进行单元测试时,即使模型无效,模型也会始终返回重定向。 问题在于,在进行单元测试时,该模型根本没有得到验证。而且由于ModelState在默认情况下是有效的,因此
ModelState.IsValid
在您的单元测试中将始终返回true,因此即使模型无效,也将始终返回重定向。 解决方案:呼叫
TryValidateModel
而不是
ModelState.IsValid
。这将迫使您的单元测试来验证模型。这种方法的一个问题是,该模型将在您的单元测试中进行一次验证,而在您的应用程序中进行两次验证。这意味着发现的任何错误将在您的应用程序中记录两次。这意味着,如果您在视图中使用
ValidationSummary
辅助方法,您将看到列出的某些重复消息。 如果这负担不了,可以在致电
TryValidateModel
之前清除clear12ѭ。这样做有一些问题,因为您将丢失一些有用的数据,例如,如果清除
ModelState
,则会丢失尝试的值,因此您可以只清除
ModelState
中记录的错误。您可以通过深入研究ѭ12并清除存储在每个项目中的每个错误来这样做,如下所示:
protected void ClearModelStateErrors()
{
    foreach (var modelState in ModelState.Values)
        modelState.Errors.Clear();
}
我已将代码放在一个方法中,以便所有Action都可以重用它。我还添加了
protected
关键字,以暗示这可能是放置在所有控制器派生自的BaseController中的有用方法,以便它们都可以访问此方法。 最终解决方案:
[HttpPost]
public ActionResult Create(Int64 id,SomeModel data)
{
    ClearModelStateErrors();

    if (ModelState.IsValid)
    {
          // TODO: Save data
         return RedirectToAction(\"Edit\",data);
    }
}
注意:我意识到我并未对根本问题有所了解。那是因为我不完全了解根本问题。如果您注意到失败的单元测试,它会因为
ControllerContext
为空而抛出
ArgumentNullException
而失败,并且将其传递给一个方法,如果
ControllerContext
为空则抛出异常。 (用他们该死的防御程序来诅咒MVC团队)。 如果您尝试模仿
ControllerContext
,您仍然会得到一个例外,这次是
NullReferenceException
。有趣的是,异常的堆栈跟踪显示两个异常都以相同的方法发生,或者我应该说构造函数位于25位。我没有源代码的副本,因此我不知道是什么导致了异常,并且我还没有找到比上面提供的解决方案更好的解决方案。我个人不喜欢我的解决方案,因为为了单元测试的好处,我正在更改编写应用程序的方式,但是似乎没有更好的选择。我敢打赌,真正的解决方案将涉及模拟其他对象,但是我只是不知道什么。 同样,在任何人获得任何聪明主意之前,嘲笑ValueProvider都不是解决方案。它会停止异常,但是您的单元测试不会验证您的模型,并且您的
ModelState
始终会报告该模型有效,即使模型无效也是如此。     ,您可能需要稍微清理一下代码:
[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\",id = id });
    }

    return RedirectToAction(\"Details\",\"Info\",new { area = \"Deals\",InfoId= id });
}
不要使用
Int64
,而应使用
int
。 至于失败的测试,我希望您的测试会一直失败,因为
TryUpdateModel
将返回false。当您从单元测试运行代码时,控制器的控制器上下文不可用,因此“ 3”将失败。 您需要以某种方式伪造/模拟
TryUpdateModel
,以使其实际上无法正常运行。相反,您“伪造”它会返回true。这里有一些帮助链接: 如何在不模拟使用UpdateModel的情况下对单元进行测试? 上面的SO答案显示了一个使用RhinoMocks的示例,它是一个免费的模拟框架。 或这个: http://www.codecapers.com/post/ASPNET-MVC-Unit-Testing-UpdateModel-and-TryUpdateModel.aspx     ,        调试测试并检查modelstate错误集合,tryupdatemodel遇到的所有错误都应该存在。