我在控制器中进行了后期操作。代码如下:
[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条件。 但是当我从整个解决方案中调试测试用例时,这个测试用例失败了,因为它进入了“ if(TryUpdateModel(data))”的其他条件。
我不知道为什么......
请帮忙......
由于
答案 0 :(得分: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
,以便它实际上不能正常运行。相反,你“伪造”它返回真实。以下是一些帮助链接:
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 :(得分: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
辅助方法,则会看到列出的重复邮件。
如果这太难以承受,您可以在致电ModelState
之前清除TryValidateModel
。这样做会有一些问题,因为如果您清除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
为空,并且如果ControllerContext
为空,它将被传递给抛出异常的方法。 (用他们该死的防守计划诅咒MVC团队)。
如果您尝试模仿ControllerContext
,您仍然会收到例外,这次是NullReferenceException
。有趣的是,异常的堆栈跟踪显示两个异常都发生在同一个方法上,或者我应该说构造函数位于System.Web.Mvc.ChildActionValueProvider
。我没有方便的源代码副本,所以我不知道是什么导致异常,我还没有找到比上面提供的更好的解决方案。我个人不喜欢我的解决方案,因为我正在改变我编写应用程序的方式以获得单元测试的好处,但似乎没有更好的选择。我打赌真正的解决方案将涉及嘲笑其他一些对象,但我只是不知道是什么。
此外,在任何人获得任何聪明的想法之前,嘲笑ValueProvider是 而不是 的解决方案。它会停止例外,但您的单元测试不会验证您的模型,并且ModelState
将始终报告模型有效,即使它不是。
答案 2 :(得分:0)
调试测试并检查modelstate错误集合,tryupdatemodel遇到的所有错误都应该在那里。