Сам репозиторий обычно не тестируется?

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

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

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

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

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

Ответ 1

В этом случае я бы строго отличался между EF и NH, и я бы не включил обе технологии в один и тот же вопрос. Простой 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)

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

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

Ответ 2

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

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

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

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

Определяет ли контекст/сеанс репозиторий?

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

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

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

Ответ 3

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

Ответ 4

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

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

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