存储库本身通常不被testing?
我很抱歉,但我是新的存储库模式,unit testing和ORM工具。
我一直在研究unit testing和存储库模式,并得出了一些结论,我想知道我是否正确。
存储库模式有助于unit testing在控制器中被replace,例如,使用它? 因为创build上下文(在EF中)或会话(在NH中)的存根/假更难,对吗? 存储库本身没有testing? 为什么?
使用EntityFramework或NHibernate与存储库模式,如果我想testing我的存储库,我需要做集成testing? 因为如果我使用我的上下文/会话的假实现,我没有做真正的testing? 因为上下文/会话本身是存储库(我的意思是他们实现了添加,删除,编辑,GetById,GetAll,..)的真实逻辑?
与EF或NH的存储库模式就像一个包装? (不仅是一个包装,我知道这是一个域的导入概念。)
在这种情况下,我会严格区分EF和NH,我不会在同一个问题中包含这两种技术。 简单的NH更成熟,并且具有导致可以更容易地testing的代码的架构。 另外在NH的情况下,你可以简单地切换到另一个数据库(如SQLite),它将仍然是一样的,而不是真正的EF的情况下切换数据库可以导致testing完全不同的应用程序 – 特别是如果您在MS和非MS数据库之间切换。
什么是存储库? 让我们来看Martin Fowler的定义 :
存储库在域和数据映射层之间进行中介,就像内存域对象集合一样。 客户端对象以声明的方式构造查询规范,并将其提交给Repository以获得满足。 对象可以从存储库中添加和删除,就像它们可以从一个简单的对象集合中获取的一样,由Repository封装的映射代码将在幕后执行适当的操作。 从概念上讲,一个存储库封装了一系列持久存储在数据存储中的对象以及对它们执行的操作,提供了持久层更加面向对象的视图。 Repository还支持在域和数据映射层之间实现干净的分离和单向依赖的目标。
好的定义。 现在想想DbSet
的目的:
- 它是否在记忆收集中起作用? 是的,你可以使用它来从数据库中获取实体,或使用
Local
属性获取已经加载的实体。 - 客户可以声明性地查询规范吗? 是的,它被称为linq-to-entities。
- 可以从collections中添加或删除对象吗? 是的,它可以。
- 映射封装了吗? 是的。
- 有没有干净的分离? 根据逻辑是。 就API而言,否,因为将
IDbSet
暴露给域模型将使域模型依赖于技术IDbSet
。 这是问题吗? 理论上是的,对于纯粹的是,但在99%,这是真的不是一个问题,因为你需要改变API的情况是罕见的,即使你正确分离的API,它总是会有很大的变化。
DbSet
是一个存储库。 直接使用DbSet
并将其包装到某个通用存储库中的唯一区别是分离。 这导致我以前的答案类似的问题 – 通用库与EF 4.1是什么意思
现在在你的应用程序中存储库的目的是什么? 我看到你之前的问题,包括你在BaseRepository
Entity框架BaseRepository
构buildBaseRepository
问题。 如果你真的认为这是一个基础知识库,它将成为你的专业知识库的父类,为你的领域模型的聚合根工作,并暴露与特定的暴露实体types相关的专门方法,那么是的 – 你正在使用知识库模式,你需要它。 但是,如果你只是包装上下文和单个集合,并调用这个仓库,那么你最有可能创build的只是具有可疑附加值的冗余层,因为这只是DbSet
包装。
在这种情况下,您的存储库( DbSet
包装器)才有意义的唯一场景是:
- 包装将永远不会公开
IQueryable
(linq-to-entities) - 包装将永远不会接受
Expression<>
并将其内部传递给IQueryalbe
(linq-to-entities)
这是唯一可以为您提供完全可嘲讽存储库的场景=>您的上层可以很容易地进行unit testing。 你不会unit testing版本库,你不会去模拟仓库中使用的上下文。 存储库包装数据访问和映射逻辑 – 存储库中唯一合理的testing是集成testing。 什么是这种情况下的问题? 你将失去LINQ的全部力量,你将不得不包装/重新实现EF中实现的一些方法和types。 这种types的存储库与使用存储过程封装数据访问时使用的相同。
如果你不遵循这种情况,你的生活会更容易。 您将有LINQ定义的查询,但是您将无法unit testing代码,因为没有模拟/伪造仍然会将查询评估为LINQ到实体。 一旦你模拟DbSet
或IQueryable
你将使用linq-to- DbSet
超集。 您可以轻松地编写一个查询,它将在DbSet
上通过testing,但在运行时会失败,并显示一个真正的DbSet
。 下面是关于这个问题的更多信息, 这里是查询的例子,它将通过testing,但在运行时失败。 在这种情况下,您必须使用集成testing(使用真正的数据库),在您的存储库上使用linq-to-entities查询的所有方法。
版本库界面属于域图层。 但是实现属于基础设施或数据访问层。 这个实现通常不用单元testing来testing。 它大量使用ORM API,所以单独testing版本库是非常困难和浪费的。 这个问题不是特定于存储库的: 不要模拟你不拥有的types。
应该使用真正的ORM,使用集成testing来testing版本库。 在内存数据库中是一个非常stream行的方法来解决这个问题
…因为如果我使用我的上下文/会话的假实现,我不做真正的testing?
即使你设法做到这一点(我在NHibernate的情况下真的怀疑),你会浪费你的时间。 会话/上下文界面超出了你的控制范围,你的testing只会重申你对真实事物的猜测。
由于上下文/会话本身是存储库?
No Context / Session是UnitOfWork模式的实现。 这不是你的域名的一部分。 这是基础设施。
EF或NH的存储库模式就像一个包装只?
存储库是重要的领域概念,它不仅仅是一个“包装”。 就像你的应用程序不是数据库的“包装”一样。 我认为DDD库的接口定义应该尽可能地基于泛在语言 ,不应该包含任何与ORM相关的词或types。
将数据库访问权限设置为轻量级包装,并将业务方法放入依赖于存储库的实体服务层是非常常见的。 因此,您可以使用包装(例如,内存数据库)的存储库来对实体服务进行unit testing。
unit testing时,您需要清楚您正在testing的“单元”。 当你testing你的单元时,你可以嘲笑你单元之外的东西。 你模拟的东西本身可以单独testing。
还有另一种testing,你testing更大的块。 所以你可能会testing你的代码+库+数据库。 这有价值,但它不是testing相同的东西。 特别是在使用真正的数据库时,很难将代码强制一些错误path。
你应该testing{你的代码+信息库}并模拟数据库吗? 取决于存储库本身的复杂程度和testing程度。