存储库本身通常不经过测试? [英] The repository itself is not usually tested?

查看:139
本文介绍了存储库本身通常不经过测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很抱歉,但我对新闻库模式,单元测试和orm工具很新。



我一直在研究单元测试和存储库模式,并得出了一些结论,我想知道我是否正确。



存储库模式有助于单元测试在控制器中被替换,例如使用它,对吗?因为创建一个存根/假的上下文(在EF中)或会话(在NH中)更难,对吗?存储库本身没有测试?为什么?



使用EntityFramework或NHibernate与存储库模式,如果我想测试我的存储库,我需要进行集成测试?因为如果我使用假的实现我的上下文/会话我没有做真正的测试?因为上下文/会话本身是存储库(我的意思是他们实现了添加,删除,编辑,GetById,GetAll,...)的真实逻辑。



存储库模式用EF或NH就像包装? (不仅是一个包装,我知道这是域的一个进口概念。)

解决方案

我将严格区分EF和在这种情况下,我不会在同一个问题中包含这两种技术。简单的NH更加成熟,并且具有可以更容易测试的代码的体系结构。另外在NH的情况下,您可以简单地将数据库切换到另一个数据库(如SQLite),并且它将仍然是相同的,在EF的情况下不需要为真,因为交换数据库可以导致测试完全不同的应用程序 - 特别是您在MS和非MS数据库之间切换。



存储库是什么?让我们看看 Martin Fowler的定义


存储库介入域和数据映射层之间,
表现为内存中的域对象集合。客户对象
以声明方式构造查询规范,并将其提交给
存储库以满足要求。对象可以添加到
的Repository中,从它们可以从一个简单的对象集合中获取
,Repository封装的映射代码将在后台执行
适当的操作。在概念上,存储库
封装了持久存储在数据存储中的一组对象,并对它们执行
操作,从而提供了一个更面向对象的视图
的持久层。存储库还支持
的目标,实现域
和数据映射层之间的干净分离和单向依赖。


很好的定义。现在考虑 DbSet 的目的:




  • 它是否作为内存集合?是的,您可以使用它从数据库中获取实体,或使用 Local 属性来获取已加载的实体。

  • 客户端可以声明式查询规范吗?是的,它被称为linq-to-entities。

  • 可以从集合中添加或删除对象吗?是的,可以。

  • 映射是否封装?是的。

  • 是否有干净的分离?就逻辑而言就API而言,由于将 IDbSet 暴露给域模型将使得域模型依赖于技术EF。是问题吗在理论上是,纯粹的是,但在99%这真的不是一个问题,因为您需要更改API的情况是罕见的,即使您正确分离API,它总是涉及大的更​​改。



DbSet 是一个存储库。直接使用 DbSet 并将其包装到某个通用存储库中的唯一区别是分离。这导致我以前对类似问题的回答 - 通用存储库与EF 4.1有什么关系



现在,应用程序中存储库的目的是什么?我看到您以前的问题,包括这一个您有您的 BaseRepository 构建在实体框架之上。如果您认真地将此作为基础存储库,那么您的专用存储库将为您的域模型的聚合根而工作,并且仅暴露与特定暴露实体类型相关的专门方法,那么是 - 您正在使用存储库模式,您需要它。但是,如果你只是包装上下文和单一集合,并调用这个存储库,你最有可能只创建了冗余的层,具有可疑的附加值,因为它只是包装在 DbSet 之上。



在这种情况下,您的存储库( DbSet 包装器)只有一种情况是有意义的:




  • 包装器永远不会公开 IQueryable (linq-to-entities)

  • 包装器将永远不会接受 Expression<> 并将其内部传递给 IQueryalbe (linq- to-entities)



这是唯一的场景,将为您提供完全可笑的仓库=>您的上层可以轻松进行单元测试。你不会去单元测试存储库,你不会模拟存储库中使用的上下文。存储库包装数据访问和映射逻辑 - 在存储库是集成测试的情况下,唯一合理的测试。这种情况有什么问题?您将失去LINQ的全部功能,您将不得不在EF中实施一些方法和类型。这种存储库与使用存储过程包装数据访问时所使用的存储库相同。



如果您不遵循这种情况,您的现场将更容易。您将有LINQ定义的查询,但您将无法对代码进行单元测试,因为没有模拟/假的,仍然会将查询作为LINQ对实体进行评估。一旦你嘲笑 DbSet IQueryable ,你将使用linq-to-object,它是linq-to-entities的超集。您可以轻松地编写一个查询,该查询将通过嘲笑的 DbSet 进行测试,但在运行时失败,使用一个真正的 DbSet 这里更多关于这个问题而这里是查询的示例,将通过测试,但在运行时失败。在这种情况下,您必须对所有方法使用集成测试(使用实际数据库),使用您的存储库顶部的linq-to-entity查询。


I'm sorry but I'm new to repositories patterns, unit tests and orm tools.

I've been researching on unit tests and the repository pattern, and arrived at some conclusions, I wonder if I'm right.

The repository pattern facilitates unit testing to be replaced in controller that make use of it for example, right? Because create a stub/fake of context(in EF) or session(in NH) is harder, right? The repository itself is not tested? Why?

Using EntityFramework or NHibernate with repository pattern, if i want to test my repositories I need to do integration tests? Because if I use a fake implementation of my context/session I'm not doing real tests? Because the context/session itself is the repository (I mean they implement the real logic of Add, Remove, Edit, GetById, GetAll, ..)?

The repository pattern with EF or NH is like a wrapper? (Not only a wrapper, I know this is a import concept of the domain.)

解决方案

I would strictly differ between EF and NH in this case and I would not include both technologies in the same question. Simple NH is more mature and has architecture leading to code which can be more easily tested. Also in case of NH you can simply switch the database to another one (like SQLite) and it will work still the same which doesn't have to be true in case of EF where switching database can result in testing completely different application - especially if you switch between MS and non-MS database.

What is the repository? Let see Martin Fowler's definition:

A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.

Nice definition. Now think about the purpose of DbSet:

  • Does it act as in memory collection? Yes you can use it to get entities from database or use Local property to get already loaded entities.
  • Can client query specifications declaratively? Yes it is called linq-to-entities.
  • Can objects be added or removed as from collection? Yes it can.
  • Is the mapping encapsulated? Yes it is.
  • Is there clean separation? In terms of logic yes. In terms of API no because exposing IDbSet to domain model will make the domain model dependent on technology - EF. Is it problem? In theory yes, for purist yes but in 99% it is really not a problem because situation where you need to change the API is rare and it always involves big changes even if you correctly separated APIs.

DbSet is a repository. The only difference between using DbSet directly and wrapping it into some generic repository is a separation. This leads to my former answer to similar question - Generic Repository With EF 4.1 what is the point

Now what is a purpose of the repository in your application? I saw your previous questions including this one where you have your BaseRepository built on top of Entity framework. If you seriously mean this as a base repository which will be parent for your specialized repositories working on aggregate roots for your domain model and exposing specialized methods related only to specific exposed entity type then yes - you are using repository pattern and you need it. But if you are just wrapping context and single set and call this repository you most probably created only redundant layer with doubtful added value because that is just wrapper on top of DbSet.

There is only single scenario where your repository (DbSet wrapper) will make sense in such case:

  • The wrapper will never expose IQueryable (linq-to-entities)
  • The wrapper will never accept Expression<> and pass it internally to IQueryalbe (linq-to-entities)

This is the only scenario which will offer you fully mockable repositories => your upper layer can be easily unit tested. You are not going to unit test repositories and you are not going to mock context used in repositories. Repositories wrap data access and mapping logic - the only reasonable tests in case of repositories are integration tests. What is problem of this scenario? You will lose whole power of LINQ and you will have to wrap / re-implement some methods and types implemented in EF. This kind of repositories is the same as used when wrapping data access using stored procedures.

If you don't follow that scenario your live will be much easier. You will have queries defined by LINQ but you will not be able to unit test the code because there is no mock / fake which will still evaluate queries as linq-to-entities. Once you mock DbSet or IQueryable you will use linq-to-object which is superset of linq-to-entities. You can easily write a query which will pass a test on mocked DbSet but fail at runtime with a real DbSet. Here is more about this problem and here is the example of query which will pass the test but fail at runtime. In this case you must use integration tests (with real database) for all methods using linq-to-entities queries on top of your repositories.

这篇关于存储库本身通常不经过测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆