Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/asp.net-mvc-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net mvc 3 TryUpdateModel导致单元测试用例出错(Asp.net mvc)_Asp.net Mvc 3_Unit Testing - Fatal编程技术网

Asp.net mvc 3 TryUpdateModel导致单元测试用例出错(Asp.net mvc)

Asp.net mvc 3 TryUpdateModel导致单元测试用例出错(Asp.net mvc),asp.net-mvc-3,unit-testing,Asp.net Mvc 3,Unit Testing,我在控制器中进行了post操作。代码如下所示 [HttpPost] public ActionResult Create(Int64 id, FormCollection collection) { var data = Helper.CreateEmptyApplicationsModel(); if (TryUpdateModel(data)) { // TODO: Save data

我在控制器中进行了post操作。代码如下所示

     [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));
    }
当我调试这个测试用例时,测试用例通过了,因为条件 if(TryUpdateModel(data))返回true并进入if条件。 但是当我调试整个解决方案中的测试用例时,这个测试用例失败了,因为它转到了else条件“if(TryUpdateModel(data))

我不知道为什么

请帮忙


感谢您调试测试并检查modelstate errors集合,tryupdatemodel遇到的所有错误都应该在那里。

您可能需要稍微清理一下代码:

[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
将失败

您需要以某种方式伪造/mock
TryUpdateModel
,以便它实际上无法正常运行。相反,你“伪造”它以返回真实。以下是一些帮助链接:

上面的答案显示了一个使用RhinoMocks的示例,RhinoMocks是一个免费的模拟框架

或者这个:


我遇到了一个类似的问题,如果您不需要使用
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);
    }
}
这不会在单元测试中引发异常,所以这是一个解决的问题。然而,现在还有另一个问题。如果您使用上述代码运行应用程序,它将正常工作。您的模型将在输入操作时进行验证,并返回正确的操作结果(重定向或视图)。但是,当您尝试对这两个路径进行单元测试时,您会发现即使模型无效,模型也会始终返回重定向

问题是,当进行单元测试时,模型根本没有得到验证。由于ModelState在默认情况下是有效的,
ModelState.IsValid在单元测试中总是返回true,因此即使模型无效,也总是返回重定向

解决方案:调用
TryValidateModel
而不是
ModelState.IsValid
。这将迫使您的单元测试验证模型。这种方法的一个问题是,这意味着模型将在单元测试中验证一次,在应用程序中验证两次。这意味着发现的任何错误都将在应用程序中记录两次。这意味着如果在视图中使用
ValidationSummary
helper方法,您将看到列出的一些重复消息

如果这太多,您可以在调用
TryValidateModel
之前清除
ModelState
。这样做会出现一些问题,因为如果清除
ModelState
,您将丢失一些有用的数据,例如尝试的值,因此您可以只清除
ModelState
中记录的错误。您可以通过深入挖掘
ModelState
并清除存储在每个项中的每个错误来完成此操作,如下所示:

protected void ClearModelStateErrors()
{
    foreach (var modelState in ModelState.Values)
        modelState.Errors.Clear();
}
我将代码放在一个方法中,这样所有操作都可以重用它。我还添加了
protected
关键字,以提示这可能是一种有用的方法,可以将其放置在所有控制器派生自的BaseController中,以便它们都可以访问此方法

最终解决方案:

[HttpPost]
public ActionResult Create(Int64 id, SomeModel data)
{
    ClearModelStateErrors();

    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);
    }
}
注意:我意识到我没有阐明根本问题。那是因为我不完全理解根本问题。如果您注意到失败的单元测试,它将失败,因为抛出了一个
ArgumentNullException
,因为
ControllerContext
为null,并且如果
ControllerContext
为null,它将被传递给一个抛出异常的方法。(用他们该死的防守编程诅咒MVC团队)

如果您试图模拟
ControllerContext
,您仍然会得到一个异常,这次是
NullReferenceException
。有趣的是,异常的堆栈跟踪显示两个异常发生在同一个方法中,或者我应该说是构造函数,位于
System.Web.Mvc.ChildActionValueProvider
。我手头没有源代码的副本,所以我不知道是什么导致了异常,我还没有找到比我上面提供的更好的解决方案。我个人不喜欢我的解决方案,因为为了单元测试的好处,我正在改变我编写应用程序的方式,但似乎没有更好的选择。我打赌真正的解决办法是模仿其他物体,但我不知道是什么

此外,在任何人有任何聪明的想法之前,嘲笑ValueProvider不是一个解决方案。它将停止异常,但您的单元测试将不会验证您的模型,并且您的
ModelState
将始终报告该模型是有效的,即使它不是有效的