Если вы хотите использовать TDD (или любой другой подход к тестированию с большим тестовым покрытием) и EF вместе, вы должны написать интеграционные или сквозные тесты. Проблема здесь в том, что любой подход с имитацией контекста или репозитория просто создает тест, который может проверить вашу логику верхнего уровня (которая использует эти макеты), но не ваше приложение.
Простой пример:
Определим общий репозиторий:
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.
Это был простой пример, но то же самое может случиться с использованием методов в запросах и другими распространенными ошибками. Более того, это также влияет на такие методы, как Add, Update, Delete, обычно доступные в репозитории. Если вы не напишете макет, который точно имитирует поведение контекста EF и ссылочную целостность, вы не будете тестировать свою реализацию.
Другая часть истории - проблемы с отложенной загрузкой, которые также трудно обнаружить с помощью модульных тестов против моков.
По этой причине вам также следует ввести интеграционные или сквозные тесты, которые будут работать с реальной базой данных с использованием реального контекста EF и L2E. Кстати. Для правильного использования 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).
- Интеграционные тесты помогают тестировать функциональность на нескольких уровнях и обычно в нескольких процессах (приложение, база данных). Вам не нужно иметь их для всего, это больше зависит от опыта, чтобы выбрать, где они будут полезны.
- Сквозные тесты - это что-то вроде проверки варианта использования / пользовательской истории / функции. Они должны охватывать весь поток требований.
Нет необходимости тестировать объект несколько раз - если вы знаете, что функция тестируется в сквозном тесте, вам не нужно писать интеграционный тест для одного и того же кода. Кроме того, если вы знаете, что этот метод имеет только один путь выполнения, который покрывается интеграционным тестом, вам не нужно писать для него модульный тест. Это намного лучше работает с подходом TDD, когда вы начинаете с большого теста (сквозного или интеграционного) и углубляетесь в модульные тесты.
В зависимости от вашего подхода к разработке вам не обязательно начинать с нескольких типов тестов с самого начала, но вы можете ввести их позже, когда ваше приложение станет более сложным. Исключением является TDD / BDD, где вы должны начать использовать как минимум сквозные и модульные тесты, прежде чем вы даже напишете одну строку другого кода.
Значит, вы задаете неправильный вопрос. Вопрос не в том, что проще? Вопрос в том, что вам поможет в конце и какая сложность подойдет вашему приложению? Если вы хотите, чтобы приложение и бизнес-логика легко тестировались на единицу, вам следует обернуть код EF в некоторые другие классы, которые можно смоделировать. Но в то же время вы должны ввести другие типы тестов, чтобы убедиться, что код EF работает.
Я не могу сказать вам, какой подход подойдет вашей среде / проекту / команде / и т. Д. Но я могу объяснить пример из своего прошлого проекта:
Я работал над проектом около 5-6 месяцев с двумя коллегами. Проект был основан на ASP.NET MVC 2 + jQuery + EFv4 и разрабатывался поэтапно и итеративно. В нем было много сложной бизнес-логики и много сложных запросов к базе данных. Мы начали с общих репозиториев и высокого покрытия кода с помощью модульных тестов + интеграционных тестов для проверки сопоставления (простые тесты для вставки, удаления, обновления и выбора объекта). Через несколько месяцев мы обнаружили, что наш подход не работает. У нас было более 1200 юнит-тестов, покрытие кода около 60% (что не очень хорошо) и множество проблем с регрессией. Изменение чего-либо в модели EF может вызвать неожиданные проблемы в частях, которые не трогались в течение нескольких недель. Мы обнаружили, что нам не хватает интеграционных или сквозных тестов для логики нашего приложения. Такой же вывод был сделан в отношении параллельной команды, работавшей над другим проектом, и использование интеграционных тестов было рассмотрено как рекомендация для новых проектов.
person
Ladislav Mrnka
schedule
10.04.2011