Asp.net mvc 3 TryUpdateModel导致单元测试用例出错(Asp.net mvc)
我在控制器中进行了post操作。代码如下所示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
[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
将失败
您需要以某种方式伪造/mockTryUpdateModel
,以便它实际上无法正常运行。相反,你“伪造”它以返回真实。以下是一些帮助链接:
上面的答案显示了一个使用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
将始终报告该模型是有效的,即使它不是有效的