Я пытаюсь понять, как правильно и эффективно выполнить юнит-тестирование моего проекта Asp.net MVC. Когда я начал этот проект, я купил Pro ASP.Net MVC, и с этой книгой я узнал о TDD и модульном тестировании. После ознакомления с примерами и того факта, что я работаю инженером-программистом в QA в моей нынешней компании, я был поражен тем, насколько крутым оказался TDD. Поэтому я начал работать над своим проектом и занялся написанием юнит-тестов для уровня своей базы данных, уровня бизнеса и контроллеров. Все прошло модульное тестирование до реализации. Сначала я думал, что это было круто, но потом дела пошли вниз.
Вот проблемы, с которыми я начал сталкиваться:
В итоге я написал код приложения, чтобы сделать возможным выполнение модульных тестов. Я не имею в виду это хорошим способом, так как мой код был сломан, и мне пришлось это исправить, чтобы пройти тестовый модуль. Я имею в виду, что абстрагирование базы данных от фиктивной базы данных невозможно из-за использования 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 / Unit, приводят неверные аргументы, утверждая, что они более эффективны при ручной отладке через IDE, или что их навыки кодирования удивительны тем, что им это не нужно. Я признаю, что оба эти аргумента являются явными ошибками, особенно для проекта, который должен обслуживаться несколькими разработчиками, но любые допустимые опровержения TDD, кажется, немногочисленны.
Итак, смысл этого поста в том, чтобы спросить, не понимаю ли я, как использовать TDD и автоматические юнит-тесты?