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.
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!
var session = Isolate.GetFake<ISession>(controller); Isolate.WhenCalled(()=>session.QueryOver<CategoryProduct>() .Where(x => x.Category == category && x.IsEnabled == true) .RowCount()) .WillReturn(1);
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)
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.