我正在使用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
?
答案 0 :(得分:3)
尽管创建虚假实现很容易,但是您的单元测试将非常不可靠,因为您的存储库暴露IQueryable<T>
并导致tight coupling并且它将始终导致特定实现泄漏
这意味着如果在单元测试中使用IQueryable<T>
上的LINQ to Objects实现,那么几乎所有通过IQueryable<T>
编写的LINQ查询都将成功,而在使用时它们可能会失败NHibernate查询提供程序。
相反,您应该使用集成测试来测试依赖于IRepository<T>
的类,这意味着您要与真实数据库进行通信,而不是内存中的替身。单元测试应该在不同的级别进行。
尽管IQueryable<T>
是一个界面,但它并不是真正的抽象,或者至少它是Leaky Abstraction;一个Dependency Inversion Principle violation。因此,您应该确保IQueryable<T>
仅在您的数据访问层中使用。
我发现的一个非常有效的解决方案是使用query handlers,其中查询对象(数据)是核心层的一部分,而他们的处理程序(使用O / RM) IQueryable<T>
)的形式是数据访问层的一部分。您可以对这些处理程序进行集成测试,而这些处理程序的使用者可以再次进行单元测试。