代码之家  ›  专栏  ›  技术社区  ›  Craig Stuntz

ASP中哪种模型绑定方法具有最佳的单元测试语义。网络MVC?

  •  5
  • Craig Stuntz  · 技术社区  · 16 年前

    定义

    在ASP中。NET MVC中,有两种方法可以在动作中进行模型绑定。让我们称之为“绑定参数方式”和“更新模型方式”。它们都做几乎完全相同的事情,而且它们的方式也几乎完全相同:

        public ActionResult UpdateWithBindArguments(Foo model)
        {
            Repository.Update(model);
            // error handling removed
            return RedirectToAction(...)
        }
    
        public ActionResult UpdateWithUpdateModel()
        {
            Foo model; 
            UpdateModel(model); // part of MVC framework
            Repository.Update(model);
            // error handling removed
            return RedirectToAction(...)
        }
    

    正如我所说的,这些几乎完全一样。第一种可能更易读,但我可以克服这一点。

    两种测试方法

    这个 重要的 我认为,区别在于 怎样 您可以对它们进行单元测试:

        [TestMethod]
        public void TestUpdateWithBindArguments()
        {
           var model = new Foo() { PropertyName = "Bar" };
           var controller = new FooController();
    
           var result = controller.UpdateWithBindArguments(model);
    
           // assert
        }
    
        [TestMethod]
        public void TestUpdateWithUpdateModel()
        {
           var formData = new FormCollection() { { "PropertyName", "Bar" } };
           var controller = new FooController();
           controller.ValueProvider = formData.ToValueProvider();
    
           var result = controller.UpdateWithUpdateModel();
    
           // assert
        }
    

    第一种方法使用强静态类型构建模型。第二种方法使用名称/值对构造提交的用户数据。我发现第一种方法更容易阅读,但第二种方法更接近网站调用控制器时的实际情况。

    由于远远超出这个问题范围的原因,我从未认为应该使用lambda表达式而不是字符串来构建aspx页面进行模型绑定。我很乐意和你讨论这个问题,但我们不要在这里讨论。就这个问题而言,让我们理所当然地认为,我将使用内置的HtmlHelper方法,这些方法接受字符串,而不是接受lambda表达式的扩展。因此,第二种方法使用名称/值对作为对aspx页面“动态”特性的非正式测试具有一定的价值。当然,它并不能取代针对网站的集成测试。

    问题(终于!)

    我看到了这两种方法的优缺点。我的问题是,有没有一个强有力的论据支持我缺少的一种方法?

    编辑 我正在寻找客观的答案。我在找不明显的 原因 为什么一种方法比另一种方法好,而不是试图进行民意调查。

    4 回复  |  直到 16 年前
        1
  •  3
  •   Haacked    16 年前

    在测试方面,在我看来,将对象直接传递给动作方法更自然。无需填充ValueProviderDictionary。

    需要另一种方法的原因是,您可能需要控制所绑定对象的实例化。DefaultModelBinder只是查找默认构造函数并调用它。

    但在某些情况下,您可能需要在将对象绑定到表单值之前自己创建对象。这就是UpdateModel发挥作用的地方。

        2
  •  0
  •   Peter Morris    16 年前

    我倾向于这样写

    public ActionResult Update(int id, string name)
    {
      Person person = PersonRepository.GetByID(id);
      //Do any error handling
      person.Name = name;
      //Do any error handling
      PersonRepository.Update(person);
    }
    

    然后这样测试(使用RhinoMocks)

    Person person = new Person();
    var mockPersonRepository = MockRepository.GenerateMock<IPersonRepository>();
    mockPersonRepository.Expect(x => x.GetByID(1)).Return(person);
    mockPersonRepository.Expect(x => x.Update(person));
    
    var controller = new MyController(mockPersonRepository);
    controller.Update(1, "Hello");
    Assert("Hello", person.Name);
    mockPersonRepository.VerifyAllExpectations();
    

    所以我的答案是,以上都不是:-)

        3
  •  0
  •   Tim Scott    16 年前

    我总是选择对上下文透明,以避免测试中出现噪音,所以第一种方法更好。

    只是好奇一下,对于HTML生成的表达式,你喜欢什么样的魔术字符串。是它们对编译器不可见,对重构的抵制,还是你讨厌智能感知。:)哦,我已经完成了,开始了一场离题辩论。

        4
  •  0
  •   James James    16 年前

    我喜欢绑定到模型对象的第一种选择。这使控制器摆脱了模型和视图的细节。因此,我可以根据需要更改模型和视图,而不会影响我的控制器。因此,测试隔离也变得更加容易。

    对于只涉及域一小部分的复杂域模型或视图,我创建了一个视图模型,对视图中的数据而不是域中的数据进行建模。然后,我编写映射代码或使用对象映射器将视图模型映射到我的域模型。这减少了我的视图和我的域之间的耦合。

    在将单个参数传递给控制器方法的问题上,我不得不不同意彼得·莫里斯的观点。这起初看起来不错,但一旦你开始使用大型表单,很快就会变得很痛苦。此外,这增加了控制器和视图之间的耦合。