4

I'm developing an application using NHibernate for the ORM, NUnit for unit testing and Ninject for my DI. I'm mocking the ISession like so:

var session = new Mock<ISession>();

With the regular non-mocked session objects I can query them with LINQ extension methods like this:

var result = Session.Query<MyEntity>();

But when I try to mock this with the following code...

session.Setup(s => s.Query<MyEntity>());

...I get a runtime "Not supported" exception:

Expression references a method that does not belong to the mocked object: s => s.Query<MyEntity>()

How can I mock basic queries like this in Moq/NHibernate?

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • Just to get idea: http://stackoverflow.com/questions/6835366/moq-linq-predicates-in-setup-method – VikciaR May 03 '13 at 12:15
  • 4
    Just a tip - don't mock ISession. What benefits would those tests give you? IMHO - write integration tests against your NH queries. For your unit tests, hide your ORM/NH with your own types/interface(s) using DAO/repositories etc instead (and mock/stub/fake these types instead). – Roger May 03 '13 at 12:50
  • 3
    @Roger The benefits it gives you is that you don't have to worry about the state of an actual database. You're **UNIT TESTING**, and that means you should only be testing the code within that method. Your test shouldn't care if someone deleted the database. That's not that method's fault. – krillgar May 20 '15 at 17:31

2 Answers2

3

Query<T>() is an extension method, that's why you can't mock it. Although @Roger answer is the way to go, sometimes it's useful to have specific tests. You can start investigating what Query<T>() method does - either by reading the NHibernate code, or using your own tests, and set the appropriate methods on ISession.

Warning: creating such a setup will make your test very fragile, and it will break, if the internal implementation of NHibernate changes.

Anyway, you can start your investigation with:

var mockSession = new Mock<ISession>(MockBehavior.Strict); //this will make the mock to throw on each invocation which is not setup
var entities = mockSession.Object.Query<MyEntity>();

The second line above is going to throw an exception and show you which actual property/method on ISession the Query<T>() extension method tries to access, so you can set it accordingly. Keep going that way, and eventually you will have a good setup for your session so you can use it in the test.

Note: I'm not familiar with NHibernate, but I have used the above approach when I had to deal with extension methods from other libraries.

Sunny Milenov
  • 21,990
  • 6
  • 80
  • 106
  • This is the best approach to discover dependencies while you are building your test fitness. +1 – Raffaeu Aug 23 '13 at 09:32
  • This doesn't answer the question about how to get around the fact that it's an extension method, and therefor unable to be mocked. – krillgar May 20 '15 at 17:33
  • @krillgar, unless I miss something obvious, the very first sentence states just that - you can't mock it using moq (the Q is tagged with moq). – Sunny Milenov May 20 '15 at 18:40
3

UPDATE for version 5:

In the new NHibernate versions Query<T> is part of the ISession interface, not an extension function, so it should be easy to mock.

Old answer:

I tried Sunny's suggestion and got this far but got stuck since the IQuery is cast to an NHibernate.Impl.ExpressionQueryImpl which is internal and I don't think can be extended. Just posting this to save other lost souls a couple hours.

var sessionImplMock = new Mock<NHibernate.Engine.ISessionImplementor>(MockBehavior.Strict);
var factoryMock = new Mock<NHibernate.Engine.ISessionFactoryImplementor>(MockBehavior.Strict);
var queryMock = new Mock<IQuery>(MockBehavior.Strict);//ExpressionQueryImpl

sessionImplMock.Setup(x => x.Factory).Returns(factoryMock.Object);
sessionImplMock.Setup(x => x.CreateQuery(It.IsAny<IQueryExpression>())).Returns(queryMock.Object);
sessionMock.Setup(x => x.GetSessionImplementation()).Returns(sessionImplMock.Object);
AlexDev
  • 4,049
  • 31
  • 36