Search code examples
nhibernatedependency-injectionmockingfluent-nhibernatestructuremap

How to mock a base class using structure map?


I am using NHibernate and have many repositories, which all inherit from a base NHibernateRepository class. Here is one of my repositories:

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));
    }
}

And the base class:

    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);
    }
}

I am now trying to mock to the base class NHibernateRepository using a test class that won't access a real database, but will instead use a static list. Here is my registration of the test class in my structure map container:

x.For(typeof(IRepository<>)).Use(typeof(TestNHibernateRepository<>));

My problem is, that the real NHibernateRepository is still used in the tests. I am using the real StaffRepository as per my registration:

x.For<IStaffRepository>().Singleton().Use<StaffRepository>();

All my other test classes are injected fine, but I think this is being problematic as it is an inherited class.

How can I make sure that my StaffRepository uses the TestNHibernateRepository instead of NHibernateRepository?


Solution

  • Although it would be easy to create a fake implementation, your unit tests will be very unreliable, because your repository exposes IQueryable<T> and that causes tight coupling and it will always cause the specific implementation to leak through.

    This means that if you use an LINQ to Objects implementation over IQueryable<T> in your unit tests, almost all LINQ queries that you write over IQueryable<T> will always succeed, while they might very well fail when using the NHibernate query provider.

    Instead, you should test classes that depend on IRepository<T> using integration testing, which means you communicate with the real database, not an in-memory stand-in. Unit testing should be done at a different level.

    Even though IQueryable<T> is an interface, it's not really an abstraction, or at least, it's a Leaky Abstraction; a Dependency Inversion Principle violation. So instead, you should ensure that IQueryable<T> is only used within your Data Access Layer.

    A very effective solution I found to this, is the use of query handlers, where the query objects (the data) is part of a core layer, while their handlers (that make use of an O/RM in the form of IQueryable<T>) are part of the Data Access Layer. You integration test those handlers, while consumers of those handlers, can again be unit tested.