Сам репозиторий обычно не тестируется? - PullRequest
11 голосов
/ 18 августа 2011

Извините, но я новичок в шаблонах репозиториев, модульных тестах и ​​инструментах orm.

Я изучал юнит-тесты и шаблон репозитория и пришел к некоторым выводам, интересно, прав ли я.

Шаблон репозитория облегчает замену модульного тестирования в контроллере, который использует его, например, верно? Потому что создать заглушку / фальшивку контекста (в EF) или сессии (в NH) сложнее, верно? Сам репозиторий не тестируется? Почему?

Используя EntityFramework или NHibernate с шаблоном репозитория, если я хочу протестировать свои репозитории, мне нужно сделать интеграционные тесты? Потому что, если я использую фальшивую реализацию моего контекста / сессии, я не делаю реальных тестов? Поскольку сам контекст / сеанс является хранилищем (я имею в виду, что они реализуют реальную логику Add, Remove, Edit, GetById, GetAll, ..)?

Шаблон хранилища с EF или NH походит на обертку? (Не только обертка, я знаю, что это концепция импорта домена.)

Ответы [ 4 ]

14 голосов
/ 18 августа 2011

Репозиторий Интерфейс относится к уровню домена. Но реализация принадлежит инфраструктуре или уровню доступа к данным. Эта реализация обычно не тестируется с unit test. Он интенсивно использует API ORM, поэтому крайне сложно и расточительно тестировать репозиторий изолированно. Эта проблема не характерна для репозиториев: Не высмеивайте типы, которые вам не принадлежат.

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

... Потому что, если я использую подделку Реализация моего контекста / сессии Я не делаю реальных тестов?

Даже если вам удастся это сделать (в чем я действительно сомневаюсь в случае NHibernate), вы будете тратить свое время. Интерфейс Session / Context находится вне вашего контроля, и ваш тест просто подтвердит ваше предположение о том, как будет работать реальная вещь.

Поскольку сам контекст / сеанс является хранилищем?

Нет. Context / Session является реализацией шаблона UnitOfWork . Это не часть вашего домена. Это инфраструктура.

Шаблон репозитория с EF или NH походит только на обертку?

Репозиторий является важной концепцией домена, это не просто «обертка». Также как ваше приложение не является «оберткой» над базой данных. Я думаю, что определение интерфейса DDD-репозитория должно быть максимально основано на Ubiquitous Language и не должно включать в себя слова или типы, связанные с ORM.

11 голосов
/ 19 августа 2011

Я бы строго отличался между EF и NH в этом случае и не включал бы обе технологии в один и тот же вопрос. Simple NH более развит и имеет архитектуру, ведущую к коду, который легче тестировать. Также в случае NH вы можете просто переключить базу данных на другую (например, SQLite), и она будет работать все так же, что не должно быть верно в случае EF, где переключение базы данных может привести к тестированию совершенно другого приложения - особенно если Вы переключаетесь между базой данных MS и не MS.

Что такое хранилище? Давайте посмотрим определение Мартина Фаулера :

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

Хорошее определение. Теперь подумайте о цели DbSet:

  • Действует ли это как при сборе памяти? Да, вы можете использовать его для получения сущностей из базы данных или использовать свойство Local для получения уже загруженных сущностей.
  • Может ли клиент запрашивать спецификации декларативно? Да, это называется linq-to-entity.
  • Можно ли добавлять или удалять объекты как из коллекции? Да, это может.
  • Является ли отображение инкапсулированным? Да, это так.
  • Есть ли чистое разделение? С точки зрения логики да. С точки зрения API нет, потому что использование IDbSet модели домена сделает модель домена зависимой от технологии - EF. Это проблема? Теоретически да, для пуристов да, но на 99% это действительно не проблема, потому что ситуация, когда вам нужно изменить API, встречается редко и всегда включает большие изменения, даже если вы правильно разделяли API.

DbSet - хранилище. Единственная разница между непосредственным использованием DbSet и переносом его в какой-либо общий репозиторий - это разделение. Это приводит к моему предыдущему ответу на аналогичный вопрос - Общий репозиторий с EF 4.1 в чем смысл

Теперь, какова цель хранилища в вашем приложении? Я видел ваши предыдущие вопросы, включая , этот , где у вас есть BaseRepository, построенный поверх платформы Entity. Если вы серьезно подразумеваете это как базовый репозиторий, который будет родительским для ваших специализированных репозиториев, работающих на совокупных корнях для вашей доменной модели и предоставляющих специализированные методы, относящиеся только к конкретному раскрытому типу сущности, тогда да - вы используете шаблон репозитория, и он вам нужен. Но если вы просто переносите контекст и одиночный набор и вызываете этот репозиторий, вы, скорее всего, создали только избыточный слой с сомнительной добавленной стоимостью, потому что это просто оболочка поверх DbSet.

Существует только один сценарий, в котором ваш репозиторий (DbSet обертка) будет иметь смысл в таком случае:

  • Оболочка никогда не будет выставлена ​​IQueryable (linq-to-entity)
  • Оболочка никогда не примет Expression<> и не передаст ее внутренне IQueryalbe (linq-to-entity)

Это единственный сценарий, который предложит вам полностью смоделированные репозитории => ваш верхний уровень может быть легко протестирован модулем. Вы не собираетесь использовать репозитории модульного тестирования и не будете имитировать контекст, используемый в репозиториях. Репозитории оборачивают логику доступа к данным и сопоставления - единственными разумными тестами в случае репозиториев являются интеграционные тесты. В чем проблема этого сценария? Вы потеряете всю мощь LINQ, и вам придется обернуть / повторно реализовать некоторые методы и типы, реализованные в EF. Этот тип репозиториев аналогичен используемому при упаковке доступа к данным с использованием хранимых процедур.

Если вы не последуете этому сценарию, ваша жизнь будет намного проще. У вас будут запросы, определенные LINQ, но вы не сможете выполнить модульное тестирование кода, потому что не существует макета / фейка, который все равно будет оценивать запросы как linq-to-entity. После того, как вы имитируете DbSet или IQueryable, вы будете использовать linq-to-object, который является надмножеством linq-to-entity. Вы можете легко написать запрос, который пройдет тест на ложном DbSet, но потерпит неудачу во время выполнения с настоящим DbSet. Здесь - больше об этой проблеме, а здесь - пример запроса, который пройдет тест, но не выполнится во время выполнения. В этом случае вы должны использовать интеграционные тесты (с реальной базой данных) для всех методов, использующих запросы linq-to-entity поверх ваших репозиториев.

4 голосов
/ 18 августа 2011

При модульном тестировании вам необходимо четко определить, какой «блок» вы тестируете.В то время как вы тестируете свое подразделение, вы высмеиваете вещи, которые находятся за пределами вашего юнита.Вещи, которые вы издеваетесь, сами могут быть проверены отдельно.

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

Стоит ли тестировать {ваш код + хранилище} и издеваться над базой данных?Зависит от того, насколько сложным и насколько хорошо протестировано хранилище само по себе.

4 голосов
/ 18 августа 2011

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

...