我正在使用NHibernate,并且有许多存储库,这些存储库都继承自基本的NHibernateRepository类。这是我的存储库之一:
public class StaffRepository : NHibernateRepository<IStaff>,
{
public IEnumerable<IStaff> GetBySiteRegionAndMonth(int siteId, int regionId, DateTime firstOfMonth)
{
return Repository.Where(ab => ab.SiteId == siteId && ab.WorkDate >= firstOfMonth && ab.WorkDate < firstOfMonth.AddMonths(1));
}
}
基类:
public class NHibernateRepository<TEntity> : IRepository<TEntity> where TEntity : IEntity
{
protected ISession session;
public NHibernateRepository()
{
this.session = new SessionCache().GetSession();
}
public IQueryable<TEntity> Query
{
get
{
return session.Query<TEntity>();
}
}
// Add
public void Add(TEntity entity)
{
session.Save(entity);
}
// GetById
public TEntity GetById(int id)
{
// return session.Load<TEntity>(id);
return this.Query.SingleOrDefault(e => e.Id == id);
}
}
我现在尝试使用一个不会访问真实数据库而是使用静态列表的测试类来模拟基类NHibernateRepository
。这是我在结构图容器中注册的测试类:
x.For(typeof(IRepository<>)).Use(typeof(TestNHibernateRepository<>));
我的问题是,测试中仍然使用真正的NHibernateRepository
。我根据我的注册使用真实StaffRepository
:
x.For<IStaffRepository>().Singleton().Use<StaffRepository>();
我所有其他测试类都注入得很好,但我认为这是有问题的,因为它是一个继承的类。
如何确保我的 StaffRepository 使用TestNHibernateRepository
而不是NHibernateRepository
?
尽管创建虚假实现很容易,但您的单元测试将非常不可靠,因为您的存储库会公开IQueryable<T>
并且会导致紧密耦合,并且总是会导致特定的实现泄漏。
这意味着,如果在单元测试中使用 LINQ to Objects 实现IQueryable<T>
,则通过IQueryable<T>
编写的几乎所有 LINQ 查询都将始终成功,而在使用 NHibernate 查询提供程序时,它们很可能会失败。
相反,您应该使用集成测试来测试依赖于IRepository<T>
的类,这意味着您与实际数据库(而不是内存中的替身(进行通信。单元测试应该在不同的级别进行。
尽管IQueryable<T>
是一个接口,但它并不是一个真正的抽象,或者至少,它是一个泄漏的抽象;一个违反依赖反转原则。因此,您应该确保仅在数据访问层中使用IQueryable<T>
。
我发现的一个非常有效的解决方案是使用查询处理程序,其中查询对象(数据(是核心层的一部分,而它们的处理程序(以IQueryable<T>
的形式使用 O/RM(是数据访问层的一部分。您可以对这些处理程序进行集成测试,而这些处理程序的使用者可以再次进行单元测试。