Подтвердить что ты не робот

Я просто не понимаю тестирование модулей TDD (проект Asp.Net MVC)?

Я пытаюсь выяснить, как правильно и эффективно unit test мой проект MVC Asp.net. Когда я начал этот проект, я купил Pro ASP.Net MVC, и с этой книгой я узнал о TDD и модульном тестировании. Увидев примеры и тот факт, что я работаю инженером-программистом в QA в своей нынешней компании, я был поражен тем, насколько удивительным TDD казалось. Поэтому я начал работать над своим проектом и пошел на gung ho для написания модульных тестов для моего уровня базы данных, бизнес-уровня и контроллеров. Все было unit test до внедрения. Сначала я подумал, что это потрясающе, но потом все стало идти вниз.

Вот проблемы, с которыми я начал сталкиваться:

  • Я закончил писать код приложения, чтобы можно было выполнить модульные тесты. Я не имею в виду это в хорошем смысле, так как в моем коде был сломан, и мне пришлось исправить его, чтобы пройти unit test. Я имею в виду, что абстрагирование базы данных до макетной базы данных невозможно из-за использования linq для извлечения данных (с использованием шаблона общего шаблона репозитория).

    Причина в том, что с linq- > sql или linq- > сущностями, которые вы можете делать, просто:

    var objs = select p from _container.Projects select p.Objects;
    

    Однако, если вы издеваетесь над уровнем базы данных, чтобы этот linq прошел unit test, вы должны изменить linq как

    var objs = select p from _container.Projects
           join o in _container.Objects on o.ProjectId equals p.Id
           select o;
    

    Это не только означает, что вы меняете логику приложения, так что вы можете unit test его, но вы делаете свой код менее эффективным с единственной целью проверки, и избавление от множества преимуществ с использованием ORM в первую очередь.

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

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

  • Написание модульных тестов для контроллеров было легко, пока я возвращал представления. Однако основная часть моего приложения (и того, который больше всего выиграет от модульного тестирования) - это сложное веб-приложение ajax. По разным причинам я решил изменить приложение на возвращение просмотров, чтобы вернуть JSON с данными, которые мне нужны. После этого мои модульные тесты стали чрезвычайно болезненными для записи, так как я не нашел хорошего способа написать модульные тесты для нетривиального json.

    После того, как я ударил голову и потратил массу времени, пытаясь найти хороший способ для unit test JSON, я сдался и удалил все тесты моего контроллера (все действия контроллера сосредоточены на этой части приложения, далеко).

  • Итак, наконец, я остался с тестированием уровня сервиса (BLL). Сейчас я использую EF4, однако у меня была эта проблема с linq- > sql. Я решил сделать первый подход к модели EF4, потому что для меня имеет смысл сделать это таким образом (определите мои бизнес-объекты и дайте структуре понять, как перевести ее в бэкэнд sql). Это было хорошо в начале, но теперь это становится громоздким из-за отношений.

    Например, у меня есть объекты Project, User и Object. Один объект должен быть связан с проектом, и проект должен быть связан с пользователем. Это не только правило конкретной базы данных, но и мои бизнес-правила. Однако, скажем, я хочу сделать unit test, что я могу сохранить объект (для простого примера). Теперь я должен сделать следующий код, чтобы убедиться, что сработало:

    User usr = new User { Name = "Me" };
    _userService.SaveUser(usr);
    
    Project prj = new Project { Name = "Test Project", Owner = usr };
    _projectService.SaveProject(prj);
    
    Object obj = new Object { Name = "Test Object" };
    _objectService.SaveObject(obj);
    
    // Perform verifications
    

    Есть много проблем, связанных с необходимостью делать все это только для выполнения одного unit test. С этим связано несколько проблем.

    • Для начала, если я добавлю новую зависимость, например, все проекты должны принадлежать категории, я должен войти в КАЖДОЕ одиночное unit test, которое ссылается на проект, добавляет код для сохранения категории, а затем добавляет код для добавления категории к проекту. Это может быть ОГРОМНОЕ усилие в пути к очень простому изменению бизнес-логики, и все же почти ни один из модульных тестов, которые я буду изменять для этого требования, на самом деле не предназначен для проверки этой функции/требований.
    • Если я затем добавлю проверки в метод SaveProject, чтобы проекты не могли быть сохранены, если у них нет имени с не менее чем 5 символами, я должен пройти через каждый объект и проект unit test, чтобы убедиться, что новое требование не приводит к сбою каких-либо несвязанных модульных тестов.
    • Если есть проблема в методе UserService.SaveUser(), это приведет к сбою всех тестов проекта и объекта, и причина этого не будет сразу заметна без необходимости выкапывать исключения.уль >

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

Я мог бы продолжать и продолжать, но до сих пор я не видел никакого способа для модульного тестирования, чтобы фактически помочь мне и не мешать мне. Я могу видеть конкретные случаи, когда я могу и, вероятно, буду выполнять модульные тесты, например, чтобы убедиться, что мои методы проверки данных работают правильно, но этих случаев мало и далеко. Некоторые из моих проблем, вероятно, могут быть смягчены, но не без добавления дополнительных слоев в мое приложение и тем самым сделать больше ошибок, чтобы я мог unit test.

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

Всюду в Интернете я вижу, как люди говорят о том, насколько велики тесты TDD, и я говорю не только о фанатичных людях. Немногие, кто отклоняет тесты TDD/Unit, дают плохие аргументы, утверждая, что они более эффективны для отладки вручную через среду IDE или что их навыки кодирования поразительны, что им это не нужно. Я признаю, что оба эти аргумента являются полными быками, особенно для проекта, который должен поддерживаться несколькими разработчиками, но любые действительные опровержения для TDD кажутся немногочисленными и далеко друг от друга.

Итак, точка этого сообщения - спросить, я просто не понимаю, как использовать TDD и автоматические модульные тесты?

4b9b3361

Ответ 1

Вы можете взглянуть на образец структуры ASP.NET MVC 2.0. Я написал. В нем представлены некоторые концепции, которые помогут вам начать работу с модулем тестирования логики контроллера и базы данных. Что касается тестирования базы данных, это уже не модульные тесты, а интеграционные тесты. Как вы увидите в моем примере, я использую NHibernate, который позволяет мне легко переключиться на образец базы данных SQLite, который был заново создан для каждого тестового оборудования.

Наконец, выполнение единичных тестов в ASP.NET MVC может быть болью без надлежащего разделения проблем и абстракций и использования насмешливых фреймворков и фреймворков, таких как MVCContrib.TestHelper может облегчить вашу жизнь.

Здесь представлен предварительный просмотр о том, как выглядит ваш контроллер unit test.


UPDATE:

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

Я мог бы дать вам совет: если вся концепция TDD трудно понять, и вы не видите никаких преимуществ в этом, тогда все в порядке. Никто не может доказать, что TDD полезен во всех ситуациях. Просто попробуйте спроектировать свое приложение таким образом, чтобы у каждого класса была одна ответственность. Если вы обнаружите, что выполняете проверку ввода, доступа к данным SQL и обработки исключений в одном классе, вы делаете это неправильно. Как только вы достигнете этого разделения, вы увидите, что модульное тестирование становится намного проще, и вы даже можете прийти на сцену, когда модульные тесты помогут вам в развитии: -)

Что касается модульного тестирования a JsonResult с MvcContrib.TestHelper, это конкретный вопрос, на который я даю конкретный ответ:

public class MyModel
{
    public string MyProperty { get; set; }
}

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return Json(new MyModel { MyProperty = "value" });
    }
}

И тест:

[TestMethod]
public void HomeController_Index_Action_Should_Return_Json_Representation_Of_MyModel()
{
    // arrange
    var sut = new HomeController();

    // act
    var actual = sut.Index();

    // assert
    actual
        .AssertResultIs<JsonResult>()
        .Data
        .ShouldBe<MyModel>("")
        .MyProperty
        .ShouldBe("value");
}

Ответ 2

Похоже, проблема заключается не в модульном тестировании, а в оснащении/платформе. Я исхожу из фона Java, поэтому мне не нужно переписывать мои запросы только для выполнения модульных тестов.

Тестирование Json - это боль в том, что вы знаете, что. Если вы не хотите его тестировать, не делайте этого:) Тестирование - это не 100% -ый охват. Он тестирует материал, который действительно нуждается в тестировании. Вы, скорее всего, получите ошибку внутри сложного выражения, чем добавление вещей на карту, а затем преобразование в json.

Если вы проверите, что карта создается правильно... есть действительно хороший шанс, что json также верен. Конечно, это не всегда будет, но вы узнаете, что как только вы запустите его, и он работает. Или нет. Это действительно так просто.

Однако я могу дать реальный комментарий к третьей проблеме. Вы действительно не хотите создавать массивные графы объектов. Но есть способы сделать это.

Создайте синглтон под названием ObjectMother. Используйте методы ObjectMother.createProject(). Внутри вы создаете экземпляр фиктивного проекта. Таким образом, 12 тестов могут использовать этот метод, и тогда вам нужно только изменить его в одном месте. Помните, что тестовый код необходимо реорганизовать!

Кроме того, вы можете посмотреть что-то вроде dbUnit. Ну, это то, что он назвал в Java World. Идея заключается в том, что до запуска каждого теста она помещает базу данных в известное состояние. И он делает это автоматически при настройке/отключении ваших тестов. Это означает, что ваш тестовый код может сразу начать тестирование. Фактические данные фиктивного теста находятся в файле script или xml.

Я надеюсь, что это поможет.

Ответ 3

Что касается тестирования ваших представлений AJAXified, я бы предложил попробовать тестовую инфраструктуру, например WatiN.

С этим вы можете сделать следующее (пример со своего сайта).

[Test]
public void SearchForWatiNOnGoogle()
{
 using (var browser = new IE("http://www.google.com"))
 {
  browser.TextField(Find.ByName("q")).TypeText("WatiN");
  browser.Button(Find.ByName("btnG")).Click();

  Assert.IsTrue(browser.ContainsText("WatiN"));
 }
}