TDD'ing MVC Контроллеры для проектирования приводов - PullRequest
6 голосов
/ 27 февраля 2010

Я начинаю новый проект (ну, перезапускаю существующий) и пытаюсь использовать TDD (в девятый раз) для всех преимуществ, которые он должен принести.

Я верю, что TDD приведет к тому, что мои тесты заставят меня писать только тот код, который мне нужно написать, но это заставит меня написать код, который мне НУЖЕН, и не пропустить его.

Вот тут и наступает мое текущее состояние неопределенности.

Рассмотрим историю:

"Пользователь должен иметь возможность добавлять виджет, поэтому они должны просматривать детали недавно добавленного виджета."

ОК, так что работа с пользовательским интерфейсом (поскольку именно там пользователь будет добавлять свой виджет, а не с помощью Visual Studio и набора сборок, которые я пишу) ... Я начну со следующего теста, написав очень минимальный так что тест проходит.

Итак, я начал с того, что контроллер выдал исключение NotImplementedException, а затем возвратил View () ... следующим было первое, что я написал наименьшее количество строк, которые я мог сделать для прохождения теста.

[TestFixture]
public class WidgetControllerTester
{

    [Test]
    public void Create_IfBusinessModelIsValid_ReturnRedirectToRouteResultToDetailsAction()
    {
    // Arrange
        var currentUser = new User
                              {
                                  DisplayName = "Fred",
                                  Email = "fred@widgets.com",
                                  Password = "pass",
                                  Status = UserStatus.Active
                              };
        var model = new WidgetModel();
        var controller = new WidgetController();
        // Act
        var actionResult = controller.Create(currentUser, model);
        // Assert
        actionResult.AssertActionRedirect().ToAction("Details");
    }
}

public class WidgetModel
{
}

public class WidgetController: Controller
{
    public ActionResult Create()
    {
        return View("Create");
    }

    [HttpPost]
    public ActionResult Create(User currentUser, Widget model)
    {
        return RedirectToAction("Details");
    }
}

Теперь я понимаю, что дополнительные тесты для недопустимых моделей и проверка состояния модели будут развиваться из дополнительных историй.

Однако я не вижу четкого пути использования дополнительных тестов для дальнейшего управления кодом в контроллере.

Например, я знаю, что в какой-то момент я захочу сделать вызов WidgetService из действия Create. Я упускаю что-то очевидное (не видя древа деревьев), как я могу усовершенствовать код контроллера с помощью дополнительных тестов?

Говоря о WidgetService, я ожидаю, что напишу WidgetServiceTester, и в данный момент ссылки в контроллере, скорее всего, будут высмеиваться.

Некоторые мысли у меня были ...

  • Создайте новый тест с именем Create_IfModelIsValid_WidgetIsAddedToRepository, но как это явно ведет к вызовам службы в действии контроллера?
  • Мне нужно написать более подробную историю о том, что модель должна быть вставлена ​​в хранилище / базу данных и т. Д.
  • Я путаю элементы TDD и XP?

Спасибо за чтение, я буду признателен за любые отзывы и идеи о наилучшей практике для развития.

Джо.

РЕДАКТИРОВАТЬ 27 февраля 2010

Я нашел следующую статью Итерация № 6 - Использование разработки через тестирование (на asp.net) (http://www.asp.net/%28S%28ywiyuluxr3qb2dfva1z5lgeg%29%29/learn/mvc/tutorial-31-cs.aspx), которая демонстрирует то, чего я добился, однако они, похоже, рассматривают добавление репозитория / службы к контроллеру как ре-факторинг ... лично я не согласен, я не прав? :)

Я подумаю над написанием теста, который проверяет ViewData действия Details и обновлю этот вопрос, как только у меня будет.

1 Ответ

1 голос
/ 28 февраля 2010

Джо. Иногда я чувствую ту же самую неопределенность. Но я также думаю, что большинство из того, что вам не хватает, - это искренние истории. Во-первых, вы должны немного разложить свою историю, чтобы создать реальные требования разработчиков. Вот что я имею в виду:

"Пользователь должен иметь возможность добавлять виджет, поэтому они должны просматривать детали недавно добавленного виджета."

Хорошо, для меня это дает следующие вопросы, которые помогут вам придумать тесты:

«Пользователь» - откуда он пришел? Как они вошли в систему? (если вы используете AccountController по умолчанию и тесты, то это уже есть, если нет, вам понадобятся тесты для получения формы входа в систему, входа в систему, проверки как успешного, так и неудачного входа в систему и т. д.)

«добавить виджет» - к чему (я не имею в виду реализацию, я просто указываю, что это подразумевает, что вы либо собираетесь использовать репозиторий или сервис, если «добавить» не означает просто добавить его) к работающему экземпляру, и вам не нужно постоянство)? должен ли виджет быть действительным на этом этапе, или недействительные виджеты могут быть сохранены и сделаны действительными позже? Это подразумевает для меня тестирование того, что в репозитории или службе есть метод hit (save (), insert (), add (), что угодно (не внутренняя часть метода), пока вы не приступите к тестированию своей службы / репо, только что контроллер выполняет свою работу, вызывая его), проверьте, что происходит с действительным / недействительным виджетом (вам нужно немного расширить свою историю или добавить историю, чтобы охватить то, что должно происходить с действительными / недействительными виджетами)

«при этом они берутся, чтобы просмотреть детали недавно добавленного виджета» - слегка перефразировано, но в основном то, что вы сказали. Всегда? или только на успехе? Является ли это представление редактируемым или доступным только для чтения (т.е. действие «Изменить» или «Подробности»)? Есть ли сообщение пользователю, в котором говорится, что он был успешным, или он должен сделать вывод из того факта, что он просматривает свой виджет, что он был успешным? Это должно запустить тесты, которые выполняют такие вещи, как проверка свойств в возвращаемом результате действия и проверка значений, хранящихся в TempData (сообщение о состоянии), а также проверка того, что происходит в обоих путях (успех или неудача).

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

Пара мыслей о вашем дизайне в будущем.

Ваш следующий тест должен сначала посмотреть на то, что я написал выше, а именно на то, что действие контроллера POST create 1) получит необходимые данные (ваши параметры), 2) вызовет этот сервис / репозиторий, чтобы «добавить» виджет, 3) возможно сделать что-нибудь, если это добавление не удастся (это в вашем дизайне; я дошел до того, что мои контролеры предполагают, что все будет хорошо, и я обрабатываю сбои с помощью атрибутов, но это личное решение проекта), 4) перенаправление на детали .

Итак, ваш следующий тест будет использовать макет (я предпочитаю библиотеку moq для кода Google, но все, что у вас есть, будет работать). Вам понадобится некоторый интерфейс для описания сервиса, который будет вызывать ваш контроллер, и вы передаете имитируемую реализацию этого тестируемому контроллеру, чтобы убедиться, что он вызывает правильный метод. В Moq это выглядело бы примерно так:

[Test] 
public void Create_CallsRepository() 
{ 
    // Arrange 
    var currentUser = new User 
                          { 
                              DisplayName = "Fred", 
                              Email = "fred@widgets.com", 
                              Password = "pass", 
                              Status = UserStatus.Active 
                          }; 
    var model = new WidgetModel(); 
    var mockService = new Mock<IService<WidgetModel>();
    mockService.Setup(s=>s.Add(model)); //.Returns(whatever) if it returns something
    var controller = new WidgetController(mockService.Object); 

    // Act 
    var actionResult = controller.Create(currentUser, model); 

    // Assert 
    mockService.Verify(s=>s.Add(model)); 
} 

Конечно, это делает некоторые предположения о дизайне, но напряженность в том, как писать свои тесты по сравнению с тем, как ваши объекты должны называться / обрабатывать вещи, является частью того, что делает TDD таким ценным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...