如何在不使用 UpdateModel 的情况下对操作进行单元测试?

发布于 2024-07-09 01:11:19 字数 1734 浏览 8 评论 0原文

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

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

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.");
}

调用 UpdateModel 引发了 ArgumentNullException ,并显示消息“Value can not”参数名称:controllerContext”。 我假设 UpdateModel 的某个地方需要 System.Web.Mvc.ControllerContext,但在测试执行期间不存在。

我还假设我在某个地方做错了事情,只需要指出正确的方向。

请帮助!

I have been working my way through Scott Guthrie's excellent post on ASP.NET MVC Beta 1. In it he shows the improvements made to the UpdateModel method and how they improve unit testing. I have recreated a similar project however anytime I run a UnitTest that contains a call to UpdateModel I receive an ArgumentNullException naming the controllerContext parameter.

Here's the relevant bits, starting with my model:

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

The controller action:

[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);
    }
  }
}

And finally my unit test that's failing:

[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 is thrown by the call to UpdateModel with the message "Value cannot be null. Parameter name: controllerContext". I'm assuming that somewhere the UpdateModel requires the System.Web.Mvc.ControllerContext which isn't present during execution of the test.

I'm also assuming that I'm doing something wrong somewhere and just need to pointed in the right direction.

Help Please!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

少女七分熟 2024-07-16 01:11:19

我认为这是不可能的,因为 UpdateModel 使用的 TryUpdateModel 引用了从单元测试调用时为 null 的 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 );

     ...
}

I don't think it can be done since TryUpdateModel, which UpdateModel uses, references the ControllerContext which is null when invoked from a unit test. I use RhinoMocks to mock or stub the various components needed by the controller.

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"] );

Here's the relevant bit from the Controller.cs source on www.codeplex.com/aspnet:

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

     ....

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

     ...
}
妖妓 2024-07-16 01:11:19

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

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

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

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

I was having this same issue. After reading tvanfosson's solution, I tried a simple solution not involving a mock framework.

Add a default ControllerContext to the controller as follows:

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

This removed the error just fine for me while unit testing. I hope this may help someone else out.

夜未央樱花落 2024-07-16 01:11:19

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

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

所以 Controller.Action 应该看起来像

public ActionResult Edit(Int32 id, CountryEdit input)
{
  var Country = input.ToDb();
  // Continue your code
}

Or you can create form data proxy, like

public class CountryEdit {
  public String Name { get; set; }
  public String Iso3166 { get; set; }
}
  • Plus. Easy create unit tests
  • Plus. Define white list of fields update from post
  • Plus. Easy setup validation rules, easy test it.
  • Minus. You should move date from proxy to you model

So Controller.Action should look, like

public ActionResult Edit(Int32 id, CountryEdit input)
{
  var Country = input.ToDb();
  // Continue your code
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文