Mocking NHibernate with Typemock Isolator is Awesome
Note: I am not affiliated in any way with Typemock
Unit testing methods that contain NHibernate calls is notoriously difficult. In fact, a common suggestion is to give up and use an in memory database instead. This is fine (I've personally tried it on multiple occasions), and it works I suppose, but has always felt more like integration testing than unit testing (what happened to "no external dependencies"?..perhaps I'm a purist) and undoubtedly slows down the test suite - especially in all but non trivial cases where reference data needs to be loaded.
I'm a novice Isolator user and messed around with the RecordExpectations syntax for a little bit before realizing just how simple Isolator makes things. Once you understand the warnings that Typemock puts out there, things get really easy. Really easy.
First Attempt
Let's say I wanted to fake the count of the products currently mapped to a specific category on my website. Normally the chained nature of NHibernate queryover calls requires mocking a LOT of stuff.
When you attempt to use Isolator's Isolate.WhenCalled method like in the case below, you may get an error message. Don't get despondent!
Code
var session = Isolate.GetFake<ISession>(controller);
Isolate.WhenCalled(()=>session.QueryOver<CategoryProduct>()
.Where(x => x.Category == category && x.IsEnabled == true)
.RowCount())
.WillReturn(1);
Error Message
There is nothing better (or more rare) than getting a useful error message. In fairness, Typemock provides just that - not only highlighting the issue, but suggesting a fix too. Wonderful!
Test 'mytest' failed:
TypeMock.ArrangeActAssert.NestedCallException :
*** WhenCalled does not support using a method call as an argument.
- To fix this pass null instead of Expression.Lambda()
***
* Example - this would work:
- MyObj argument = Something.Other().GetStuff();
- Isolate.WhenCalled(() => ObjUnderTest.MethodUnderTest(argument))...;
***
* Example - this would not work:
- Isolate.WhenCalled(() => ObjUnderTest.MethodUnderTest(Something.Other().GetStuff()))...;
at cx.a()
at g4.a()
at bh.a(Boolean A_0)
at dl.b(Boolean A_0)
at ij.b(Boolean A_0)
at ij.a(Object A_0, Boolean A_1, Func`1 A_2, Action A_3, Action A_4, Action A_5, Boolean A_6)
at ij.e(Object A_0)
at TypeMock.ArrangeActAssert.ExpectationEngine`1.a(TResult A_0)
Updated Syntax
The issue here is that in the chained Where call I am using a lambda expression to filter my query. Since Typemock doesn't support methods being sent as arguments (and my lambda here is nothing more than an inline anonymous function) I need to break the lambda out into a standalone expression (Resharper is your friend).
What I end up with is the following terse (certainly compared to the alternative) syntax to mock a chained NHibernate call.
Expression<Func<CategoryProduct, bool>> expression = x => x => x.Category == category && x.IsEnabled == true;
Isolate.WhenCalled(()=>session.QueryOver<CategoryProduct>().Where(expression).RowCount()).WillReturn(1);
While Isolator is pretty pricey, it's this kind of power that separates it from the pack. I defy anyone to show me a better/easier way to do this.
Comments