So I'm trying to write some test cases for my business logic layer. I've already mocked up my data access layer (which returns NHibernate IQueryOver
objects). I created a MockQueryOver
class that implements the IQueryOver
interface because I chain functions in the business logic layer so creating a stubbed IQueryOver
didn't make sense to me.
Anyway, that all works, but the problem I'm having is when I try to do an OrderBy()
on the QueryOver
. In my MockQueryOver
class, I implement the OrderBy()
method like this right now:
public IQueryOverOrderBuilder<TRoot, TSubType> OrderBy(Expression<Func<TSubType, object>> path)
{
var func = path.Compile();
IList<TSubType> result = m_data.OrderBy(func).ToList();
var mockRepo = new MockRepository();
var queryOver = new MockQueryOver<TRoot, TSubType>(m_data);
IQueryOverOrderBuilder<TRoot, TSubType> mockOrderBuilder = mockRepo.StrictMock<IQueryOverOrderBuilder<TRoot, TSubType>>(queryOver, path);
mockOrderBuilder.Stub(x => x.Desc).Return(queryOver);
mockOrderBuilder.Stub(x => x.Asc).Return(queryOver);
return mockOrderBuilder;
}
The problem is that RhinoMocks throws an exception on any of the Stub methods. This is the exception:
System.NullReferenceException : Object reference not set to an instance of an object.
at NHibernate.Criterion.Lambda.QueryOverOrderBuilderBase`3.AddOrder(Func`2 orderStringDelegate, Func`2 orderDelegate)
at NHibernate.Criterion.Lambda.QueryOverOrderBuilderBase`3.get_Desc()
at NHibernate.Criterion.QueryOverBuilderExtensions.Desc(IQueryOverOrderBuilder`2 builder)
at BLL.Tests.Mock_Objects.MockQueryOver`2.<OrderBy>b__7(IQueryOverOrderBuilder`2 x) in MockQueryOverSubType.cs: line 239
I'm new to NHibernate and RhinoMocks, so I'm not sure what it's doing behind the scenes, but it seems like even though I'm creating a mock of an interface, it still calls concrete extension methods when I try to stub a method.
Can someone please clarify this or help me out with this problem? Also, since I'm just beginning to write these test cases, I don't mind switching mocking frameworks, as long as it's free to use.
Thanks a lot!
Are you certain IQueryOverOrderBuilder
is an interface? It seems to be a confusingly-named class that implements QueryOverOrderBuilderBase. I'm not entirely sure what the behaviour is in this situation, but I think your StrictMock
of IQueryOverOrderBuilder
is actually calling through to that base class, which probably isn't set up, and is throwing the exception you're seeing.
I think that perhaps you might need add another abstraction layer in between your business logic and NHibernate, with classes that you can reliably mock. I don't think there's a way of mocking a concrete class like IQueryOverOrderBuilder
with RhinoMocks (but I'm happy to be corrected if there is :).
To create another abstraction layer, analyse the operations in your business logic that currently interact with NHibernate, and define a new interface of functions to encapsulate those operations (say, IRepository). Code that adds something to the database through NHibernate might become a function on the interface named AddItem
. Move the code that interacts with NHibernate behind this interface into new functions on a new class (there's no reason why it needs to be a single class - you could group logically-related code into separate classes with separate interfaces). The new interface might be able to reference some NHibernate classes and interfaces that can be easily instantiated or mocked, respectively (ideally, the interface wouldn't reference NHibernate at all, but you're doing this for testing, not to fully decouple your code from NHibernate, so it's fine). Once you've done this, your business logic can be unit-tested against a mock (or mocks) of the new interface, and the class or classes that implement that interface can be integration tested against an actual database. This is, loosely, the Adapter pattern. Without seeing exactly what your business logic does, it's difficult to comment further on the design. I hope this is helpful.
Finally, if you're creating your own MockRepository
, I think you need to call Replay()
on the mocks you create (or ReplayAll()
on the MockRepository
) after you stub them. You don't need to do this if you create your mocks from the static methods on MockRepository
. Seems unrelated to your current issue (although, try calling it after your Stub
calls and see if it makes any difference), but I thought I'd mention it anyway.