0

我正在使用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

回答

3

尽管创建假实现很容易,但是您的单元测试将非常不可靠,因为您的存储库公开了IQueryable<T>,并且这导致了tight coupling,并且它总是会导致特定实现泄露。

这意味着,如果你在你的单元测试过IQueryable<T>使用LINQ到对象的实现,几乎你写了IQueryable<T>所有的LINQ查询总是会成功,而他们可能使用NHibernate的查询提供程序时非常好失败。

相反,你应该测试依赖于IRepository<T>使用集成测试,这意味着你与真正数据库进行通信,而不是在内存中的替身类。单元测试应该在不同的级别完成。

即使IQueryable<T>是一个接口,它不是一个真正的抽象,或者至少,它是一个Leaky Abstraction;一个Dependency Inversion Principle violation。相反,您应该确保IQueryable<T>仅用于您的数据访问层。

我发现的一个非常有效的解决方案是使用query handlers,其中查询对象(数据)是核心层的一部分,而它们的处理程序(使用O/RM的形式IQueryable<T>)是数据访问层的一部分。您可以集成测试这些处理程序,而这些处理程序的使用者可以再次进行单元测试。