Какой метод связывания моделей имеет лучшую семантику модульного теста в ASP.NET MVC? - PullRequest
5 голосов
/ 06 февраля 2009

Определения

В 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
    }

Первый метод создает модель с использованием строгой статической типизации. Вторая конструирует представленные пользовательские данные с парами имя / значение. Я считаю, что первый метод немного проще для чтения, но второй метод гораздо ближе к тому, что происходит на самом деле, когда контроллер вызывается веб-сайтом.

По причинам, выходящим далеко за рамки этого вопроса, я никогда не был убежден, что нужно создавать страницы aspx, используя лямбда-выражения вместо строк для привязки модели. Я был бы рад провести с вами эту дискуссию, но давайте не будем делать это здесь. Для целей этого вопроса давайте считать само собой разумеющимся, что я буду использовать встроенные методы HtmlHelper, которые принимают строки вместо расширений, которые принимают лямбда-выражения. Поэтому использование второго метода пар имя / значение имеет некоторое значение в качестве неформального теста на «динамический» характер страниц aspx. Конечно, он не заменяет интеграционное тестирование на сайте.

Вопрос (наконец-то!)

Я вижу преимущества и недостатки обоих методов. У меня вопрос, есть ли действительно сильный аргумент в пользу одного метода, который мне не хватает?

Редактировать Я ищу объективные ответы. Я ищу неочевидные причины , почему один метод лучше другого, не пытаясь принять участие в опросе.

Ответы [ 4 ]

3 голосов
/ 07 февраля 2009

С точки зрения тестирования, мне кажется, что передача объекта непосредственно в метод действия более естественна. Нет необходимости заполнять ValueProviderDictionary.

Причина, по которой нужен другой подход, заключается в том, что вам может потребоваться управлять созданием объекта, который вы связываете. DefaultModelBinder просто ищет конструктор по умолчанию и вызывает его.

Но в некоторых случаях вам может потребоваться создать объект самостоятельно, прежде чем связать его со значениями формы. Вот где в игру вступает UpdateModel.

0 голосов
/ 06 февраля 2009

Я бы всегда выбрал прозрачность для контекста, чтобы не допустить шума в тестах, поэтому первый метод лучше IMO.

Просто любопытно, что вам нравится в магических строках над выражениями для генерации HTML. Это их невидимость для компилятора, сопротивление рефакторингу или, может быть, вы ненавидите intellisense. :) Ой, я сделал это сейчас, начал дебаты не по теме.

0 голосов
/ 07 февраля 2009

Мне нравится первый вариант привязки к модельному объекту. Это освобождает контроллер от деталей модели и вида. Поэтому я могу изменять модель и вид по мере необходимости, не влияя на мой контроллер. Из-за этого тестовая изоляция также становится легче.

Для сложных моделей домена или представлений, которые касаются только части домена, я создаю модель представления, которая моделирует данные в моих представлениях вместо данных в моем домене. Затем я пишу код сопоставления или использую сопоставитель объектов для сопоставления модели представления с моей моделью предметной области. Это уменьшает связь между моим видом и моим доменом.

Мне придется не согласиться с Питером Моррисом в отношении передачи отдельных параметров методу контроллера. Это выглядит хорошо сначала, но быстро становится болью, как только вы начинаете работать с большими формами. Кроме того, это увеличивает связь между контроллером и представлением.

0 голосов
/ 06 февраля 2009

Я был бы склонен написать это так

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();

Итак, мой ответ: ничего из вышеперечисленного: -)

...