Разве я просто не понимаю поблочное тестирование TDD (Asp. Сетевой проект MVC)?

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

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

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

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

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

    Однако при насмешке слоя базы данных чтобы иметь это, linq передают модульный тест, которым необходимо изменить linq, чтобы быть

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

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

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

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

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

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

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

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

    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
    

    Существует много проблем с необходимостью сделать все это только для выполнения одного модульного теста. Существует несколько проблем с этим.

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

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

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

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

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

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

16
задан KallDrexx 20 May 2010 в 04:33
поделиться

3 ответа

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

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

Вот предварительный просмотр того, как может выглядеть юнит-тест вашего контроллера.


UPDATE:

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

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

Что касается модульного тестирования 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");
}
6
ответ дан 30 November 2019 в 23:14
поделиться

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

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

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

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

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

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

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

3
ответ дан 30 November 2019 в 23:14
поделиться

Что касается тестирования ваших 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"));
 }
}
1
ответ дан 30 November 2019 в 23:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: