8

我一直在阅读 Scott Guthrie 在ASP.NET MVC Beta 1上的出色文章。在其中,他展示了对 UpdateModel 方法的改进以及它们如何改进单元测试。我重新创建了一个类似的项目,但是每当我运行包含对 UpdateModel 的调用的 UnitTest 时,我都会收到一个 ArgumentNullException 命名 controllerContext 参数。

以下是相关位,从我的模型开始:

public class Country {
  public Int32 ID { get; set; }
  public String Name { get; set; }
  public String Iso3166 { get; set; }
}

控制器动作:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Int32 id, FormCollection form)
{
  using ( ModelBindingDataContext db = new ModelBindingDataContext() ) {
    Country country = db.Countries.Where(c => c.CountryID == id).SingleOrDefault();

    try {
      UpdateModel(country, form);

      db.SubmitChanges();

      return RedirectToAction("Index");
    }
    catch {
      return View(country);
    }
  }
}

最后我的单元测试失败了:

[TestMethod]
public void Edit()
{
  CountryController controller = new CountryController();
  FormCollection form = new FormCollection();
  form.Add("Name", "Canada");
  form.Add("Iso3166", "CA");

  var result = controller.Edit(2 /*Canada*/, form) as RedirectToRouteResult;

  Assert.IsNotNull(result, "Expected to be redirected on successful POST.");
  Assert.AreEqual("Show", result.RouteName, "Expected to redirect to the View action.");
}

ArgumentNullException调用时抛出UpdateModel消息“值不能为空。参数名称:controllerContext”。我假设某处UpdateModel需要System.Web.Mvc.ControllerContext在测试执行期间不存在的地方。

我还假设我在某处做错了什么,只需要指出正确的方向。

请帮忙!

4

3 回答 3

5

我不认为它可以完成,因为 UpdateModel 使用的 TryUpdateModel 引用了从单元测试调用时为空的 ControllerContext。我使用 RhinoMocks 模拟或存根控制器所需的各种组件。

var routeData = new RouteData();
var httpContext = MockRepository.GenerateStub<HttpContextBase>();
FormCollection formParameters = new FormCollection();

EventController controller = new EventController();
ControllerContext controllerContext = 
    MockRepository.GenerateStub<ControllerContext>( httpContext,
                                                    routeData,
                                                    controller );
controller.ControllerContext = controllerContext;

ViewResult result = controller.Create( formParameters ) as ViewResult;

Assert.AreEqual( "Event", result.Values["controller"] );
Assert.AreEqual( "Show", result.Values["action"] );
Assert.AreEqual( 0, result.Values["id"] );

以下是 www.codeplex.com/aspnet 上 Controller.cs 源代码中的相关信息:

protected internal bool TryUpdateModel<TModel>( ... ) where TModel : class
{

     ....

    ModelBindingContext bindingContext =
           new ModelBindingContext( ControllerContext,
                                    valueProvider,
                                    typeof(TModel),
                                    prefix,
                                    () => model,
                                    ModelState,
                                    propertyFilter );

     ...
}
于 2008-12-01T20:27:52.837 回答
3

我遇到了同样的问题。在阅读了 tvanfosson 的解决方案后,我尝试了一个不涉及模拟框架的简单解决方案。

将默认 ControllerContext 添加到控制器,如下所示:

CountryController controller = new CountryController();
controller.ControllerContext = new ControllerContext();

这消除了单元测试时对我来说很好的错误。我希望这可以帮助其他人。

于 2010-12-03T20:47:54.900 回答
0

或者您可以创建表单数据代理,例如

public class CountryEdit {
  public String Name { get; set; }
  public String Iso3166 { get; set; }
}
  • 加。轻松创建单元测试
  • 加。定义从帖子更新的字段白名单
  • 加。轻松设置验证规则,轻松测试。
  • 减。您应该将日期从代理移动到您的模型

所以 Controller.Action 应该看起来像

public ActionResult Edit(Int32 id, CountryEdit input)
{
  var Country = input.ToDb();
  // Continue your code
}
于 2010-03-31T10:28:59.393 回答