我目前正在为MVC4应用程序中的存储库实现编写单元测试.为了模拟数据上下文,我开始采用这篇文章中的一些想法,但我现在发现了一些限制,让我怀疑是否有可能正确模拟IQueryable.
特别是,我已经看到了一些测试通过但代码在生产中失败的情况,并且我无法找到任何方法来模拟导致此失败的行为.
例如,以下代码段用于选择Post属于预定义类别列表的实体:
var posts = repository.GetEntities<Post>(); // Returns IQueryable<Post>
var categories = GetCategoriesInGroup("Post"); // Returns a fixed list of type Category
var filtered = posts.Where(p => categories.Any(c => c.Name == p.Category)).ToList();
Run Code Online (Sandbox Code Playgroud)
在我的测试环境中,我试图嘲弄posts使用假DbSet上面提到的实施,同时也通过创建List的Post实例并将其转换为IQueryable使用AsQueryable()扩展方法.这两种方法都在测试条件下工作,但代码实际上在生产中失败,但有以下例外:
System.NotSupportedException : Unable to create a constant value of type 'Category'. Only primitive types or enumeration types are supported in this context.
虽然像这样的LINQ问题很容易解决,但真正的挑战是找到它们,因为它们不会在测试环境中显示出来.
我期望我可以嘲笑实体框架的实施行为,这是不现实的IQueryable吗?
谢谢你的想法,
蒂姆.
我正在开发一个工作单元实现,它在Entity Framework 4.1和NHibernate中都有效.在下面找到我的实现细节的框架
IUnitOfWork定义
public interface IUnitOfWork
{
IRepository<LogInfo> LogInfos { get; }
IRepository<AppInfo> AppInfos { get; }
void Commit();
void Rollback();
}
Run Code Online (Sandbox Code Playgroud)
IRepository定义
public interface IRepository<T> where T : class, IEntity
{
IQueryable<T> FindAll();
IQueryable<T> FindWhere(Expression<Func<T, bool>> predicate);
T FindById(int id);
void Add(T newEntity);
void Remove(T entity);
}
Run Code Online (Sandbox Code Playgroud)
在NHibernate中实现UoW
public class NHibernateUnitOfWork : IUnitOfWork, IDisposable
{
public ISession Session { get; private set; }
public NHibernateUnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
Session = _sessionFactory.OpenSession();
_transaction = Session.BeginTransaction();
}
public …Run Code Online (Sandbox Code Playgroud) 好吧,不确定这是否是正确的标题,但基本上我在使用MVC应用程序中的存储库时遇到了很多问题,您可以用另一组存储库替换一组存储库,实现不同的数据存储技术.
例如,假设我想为我的应用程序使用Entity Framework.但是,我还希望在硬编码列表中实现一组测试数据.我想有一组接口(IUserRepository,IProductRepository等 - 我们暂不讨论现在更通用的IRepository <T>)这两种方法都可以实例化.然后,使用(比方说)Ninject或Castle Windsor等依赖注入工具,我可以在实体框架提供程序(访问实际数据库)和测试提供程序(访问列表)之间来回切换.
简而言之,问题在于:
- 如果要使用Entity Framework,则希望您的存储库返回IQueryable <SomeType>.
- 如果你打算使用硬编码列表,你不希望你的存储库返回IQueryable,因为它大大增加了开销,而且,Linq to Entities与Linq到Objects有很大的不同,导致代码中的许多麻烦这两个提供商都很常见.
换句话说,我发现最好的方法是隔离存储库中所有依赖于EF的代码,以便存储库本身返回IEnumerable或IList或其他类似的东西 - 然后EF和其他一些技术都可以使用相同的存储库.因此,所有IQueryable都将包含在EF存储库中.这样,您可以将Linq实体与EF存储库一起使用,将Linq添加到具有测试存储库的对象.
然而,这种方法将大量的业务逻辑放入存储库中,并导致许多重复的代码 - 即使实现有些不同,逻辑也必须在每个存储库中重复.
存储库作为这个非常薄的层并且只是连接到数据库的整个想法随后丢失 - 存储库是业务逻辑的"存储库"以及数据存储连接.你不能只有查找,保存,更新等.
我无法解决需要隔离提供程序相关代码和在集中位置拥有业务逻辑之间的这种差异.
有任何想法吗?如果有人能够指出一个解决这个问题的实现的例子,我将非常感激.(我已经阅读了很多内容,但找不到任何专门讨论这些问题的内容.)
更新:
我想我开始觉得可能不可能为不同的提供者换出存储库 - 例如,如果你要使用Entity Framework,你只需将整个应用程序用于实体框架.单元测试?我正在努力解决这个问题.到目前为止,我的做法是使用硬编码数据建立一个单独的存储库,并将其用于单元测试,以及在设置数据库之前测试应用程序本身.我想我将不得不寻找一个不同的解决方案,也许是一些嘲弄工具.
但后来提出了为什么使用存储库的问题,特别是为什么使用存储库接口.我正在研究这个问题.我认为确定最佳实践是需要进行一些研究.
asp.net-mvc design-patterns entity-framework repository-pattern