Sunday, 5 February 2012

Mock Linq Extension method in NHibernate ISession

There have been some discussion about the downside of repository layer in software design as Ayende Rahien posted in one of his blog. He suggested that with debut of modem ORM technologies, there is no need to have repository. His main argument is that repository layer adds complexities to the design and prevent developer access all the features of ORMs.

I agree with him. From my past experience with NHibernate and Entity Framework, adding a repository also creates a rather leaky repository layer, and sometimes performance takes a hit too.

However, there are still many reasons why people are still using Repository, two frequently mentioned are:

1. It is easier to use a differnt ORM technology later on.
2. We can easily mock repository for unit test.

Well, for #1, I don't think it is much of a issue. NHibernate and Entity Framework (as of version 4.2) are both quite mature. For me once I choose which one to use for a project, I never switch to use another one, simply because there is no need, the cost of switching to another ORM outweights the benefit.

I think #2 is more valid. I find entity framework is impossible to mock. It does not provide enough interfaces to substitute with my own implementation.  NHibernate is a lot better, so I decide to give it a try to see if I can mock ISession.

Personally I mostly use ISession linq extension methods querying data, so if I could mock these extension method, I would very happy to drop repository pattern. The problem with extension methods is that they are not actually a part of interface signature. Mocking framework will not be able to mock them (It will throw a exception at runtime).

Luckily I found a very good blog post by Daniel Cazzulino. Basicaly,  his idea is to have factory method to provide implementation for a extension method, and expose the the factory method as a public func property (delegate) so that we can substitute with our own version of func for testing purpose. Now, the only problem is that LinqExtensionMethods class in NHibernate does not provide public func property that you can access, I even dig into the source code and I found that there is no way I can mock ISession linq extension method directly.

So what about mocking it indirectly? After some head scratching, I realised that I can always write my own extension methods that wraps the original extension methods and expose a factory method to create the implementation. Here is how (based on NHibernate 3.2):


    public static class Extensions
    {
        internal static Func<ISessionIQueryable<object>> CreateIQueryable;
        
        public static IQueryable<T> LinqQuery<T>(this ISession session)
        {
            if (CreateIQueryable == null)
                return session.Query<T>();
            else
                return CreateIQueryable(session).Cast<T>();
        }
    }


So LinqQuery is now my accessing point for querying data, it wraps original Query<T> method inside.  If your want to mock LinqQuery for testing, you can simply do:


            List<Company> companies = new List<Company>
            {
                new Company() {ID = 1, Name = "Company A"},
                new Company() {ID = 2, Name = "Company" B}
            };
            Extensions.CreateIQueryable = session => companies.AsQueryable();


Happy mocking!



No comments:

Post a Comment