Ответ 1
Если вы хотите использовать TDD (или любой другой подход к тестированию с высоким охватом тестирования) и EF вместе, вы должны написать интеграцию или сквозные тесты. Проблема в том, что любой подход с издевательством либо контекста, либо репозитория просто создает тест, который может протестировать вашу логику верхнего уровня (которая использует эти mocks), но не ваше приложение.
Простой пример:
Определить общий репозиторий:
public interface IGenericRepository<TEntity>
{
IQueryable<TEntity> GetQuery();
...
}
И давайте напишем какой-нибудь бизнес-метод:
public IEnumerable<MyEntity> DoSomethingImportant()
{
var data = MyEntityRepo.GetQuery().Select((e, i) => e);
...
}
Теперь, если вы издеваетесь над репозиторием, вы будете использовать Linq-To-Objects, и у вас будет зеленый тест, но если вы запустите приложение с Linq-To-Entities, вы получите исключение, потому что выбор перегрузки с индексами не поддерживается в L2E.
Это был простой пример, но это может произойти с использованием методов в запросах и других распространенных ошибках. Кроме того, это также затрагивает такие методы, как "Добавить", "Обновить", "Удалить", обычно открытые в репозитории. Если вы не напишете макет, который будет точно имитировать поведение контекста EF и ссылочную целостность, вы не будете тестировать свою реализацию.
Другая часть истории - проблемы с Lazy-загрузкой, которые также вряд ли можно обнаружить с помощью модульных тестов против mocks.
Из-за этого вы также должны ввести интеграционные или сквозные тесты, которые будут работать с реальной базой данных, используя реальный контекст EF L2E. Btw. для использования TDD требуется использование сквозных тестов. Для написания сквозных тестов в ASP.NET MVC вы можете WatiN и, возможно, SpecFlow для BDD, но это действительно добавит много работы, но вы действительно проверите свое приложение. Если вы хотите больше узнать о TDD, я рекомендую эту книгу (единственный недостаток заключается в том, что примеры на Java).
Интеграционные тесты имеют смысл, если вы не используете общий репозиторий и скрываете свои запросы в каком-то классе, который не будет выставлять IQueryable
, а возвращает непосредственно данные.
Пример:
public interface IMyEntityRepository
{
MyEntity GetById(int id);
MyEntity GetByName(string name);
}
Теперь вы можете просто написать интеграционный тест, чтобы протестировать реализацию этого репозитория, потому что запросы скрыты в этом классе и не подвергаются верхним уровням. Но этот тип репозитория как-то рассматривается как старая реализация, используемая с хранимыми процедурами. Вы потеряете много функций ORM с этой реализацией или вам придется сделать много дополнительной работы - например, добавьте шаблон спецификации, чтобы иметь возможность для определения запроса в верхнем слое.
В ASP.NET MVC вы можете частично заменить сквозные тесты интеграционными тестами на уровне контроллера.
Изменить на основе комментария:
Я не говорю, что вам нужны модульные тесты, интеграционные тесты и сквозные тесты. Я говорю, что для тестирования приложений требуется гораздо больше усилий. Количество и типы необходимых тестов зависят от сложности вашего приложения, ожидаемого будущего приложения, ваших навыков и навыков других членов команды.
Проекты с небольшим удалением могут быть созданы без тестов вообще (хорошо, это не очень хорошая идея, но мы все это делали, и в конце это сработало), но как только проект проходит какую-то сделку, вы можете обнаружить, что вводя новые функции или поддерживая проект очень тяжелый, потому что вы никогда не уверены, что он сломал что-то, что уже работало - это называется регрессией. Лучшая защита от регрессии - хороший набор автоматических тестов.
- Единичные тесты помогают вам протестировать метод. Такие тесты должны идеально охватывать все пути выполнения в методе. Эти тесты должны быть очень короткими и легкими в написании - сложной части может быть настройка зависимостей (mocks, faktes, stubs).
- Интеграционные тесты помогают тестировать функциональность по нескольким уровням и, как правило, через несколько процессов (приложение, база данных). Вам не нужно иметь их для всего, больше о опыте, чтобы выбрать, где они полезны.
- Конечные тесты - это что-то вроде проверки истории использования/случая использования/пользователя. Они должны охватывать весь поток требований.
Нет необходимости многократно проверять фету - если вы знаете, что эта функция протестирована в сквозном тесте, вам не нужно записывать интеграционный тест для того же кода. Также, если вы знаете, что метод имеет только один путь выполнения, который покрывается интеграционным тестом, вам не нужно писать unit test для него. Это намного лучше работает с TDD-подходом, где вы начинаете с большого теста (от конца до конца или интеграции) и углубляетесь в модульные тесты.
В зависимости от вашего подхода к разработке вы не должны начинать с нескольких типов тестов с начала, но позже можете их представить, так как ваше приложение станет более сложным. Исключением является TDD/BDD, где вы должны начать использовать, по крайней мере, сквозные и модульные тесты, прежде чем писать одну строку другого кода.
Итак, вы задаете неправильный вопрос. Вопрос не в том, что проще? Вопрос в том, что поможет вам в конце и какая сложность подходит для вашего приложения? Если вы хотите легко протестировать приложение и бизнес-логику, вы должны перенести EF-код на другие классы, которые можно издеваться. Но в то же время вы должны ввести другие типы тестов для обеспечения работы EF-кода.
Я не могу сказать, какой подход подходит вашей среде/проекту/команде/и т.д. Но я могу объяснить пример из моего прошлого проекта:
Я работал над проектом около 5-6 месяцев с двумя коллегами. Проект был основан на ASP.NET MVC 2 + jQuery + EFv4, и он был разработан поэтапно и итеративно. У него было много сложной бизнес-логики и множество сложных запросов к базе данных. Мы начали с общих хранилищ и высокого покрытия кода с помощью модульных тестов + интеграционных тестов для проверки соответствия (простые тесты для вставки, удаления, обновления и выбора объекта). Через несколько месяцев мы обнаружили, что наш подход не работает. У нас было более 1.200 единиц тестов, охват кода около 60% (что не очень хорошо) и много проблем с регрессией. Изменение чего-либо в модели EF может привести к неожиданным проблемам в деталях, которые не были затронуты в течение нескольких недель. Мы обнаружили, что нам не хватает интеграционных тестов или сквозных тестов для нашей прикладной логики. Тот же вывод был сделан в отношении параллельной команды, работавшей над другим проектом, и использование интеграционных тестов рассматривалось как рекомендация для новых проектов.