views:

632

answers:

4

Hello, I am starting a new project with NHibernate, ASP.NET MVC 2.0 and StructureMap and using NUnit and Moq for testing. For each of my controllers I have a single public constructor into which an ISession is being injected. The application itself works just fine, but in terms of unit testing I essentially have to mock an ISession in order to test the controllers.

When I attempt to Mock the ISession with MOQ i get the following error message:

Only property accesses are supported in intermediate invocations

It appears that my problem is expecting List of users from the framework CreateQuery method but after googling the issue I am now clearer.

I have two questions:

1) Is this the WRONG way to mock dependency injection of an ISession

2) Is there a way to modify the code so that it can successfully return my list

            [Test]
            public void DummyTest()
            {

                var mock = new Mock<ISession>();
                var loc = new Mock<User>();
                loc.SetupGet(x => x.ID).Returns(2);
                loc.SetupGet(x => x.FirstName).Returns("John");
                loc.SetupGet(x => x.LastName).Returns("Peterson");

                var lst = new List<User> {loc.Object};
                mock.Setup(framework => framework.CreateQuery("from User").List<User>()).Returns(lst);

                var controller = new UsersController(mock.Object);
                var result = controller.Index() as ViewResult;
               Assert.IsNotNull(result.ViewData);
            }

Please note, I am pretty sure I could just create a hard-coded list of users (rather than mocking an individual User and adding it to a list) but figured I'd leave the code as I have it right now.

Also, the Index action of this particular controller essentially executes the CreateQuery call mimicked above to return all users in the database. This is a contrived example - don't read anything into the details.

Thanks in advance for your help

Edit: In reply to the below comment, I am adding the stacktrace for the error. Also, all properties on the User class are virtual.

TestCase 'Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView' failed: System.NotSupportedException : Only property accesses are supported in intermediate invocations on a setup. Unsupported expression framework.CreateQuery("from User"). at Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression m) at Moq.ExpressionVisitor.Visit(Expression exp) at Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression m) at Moq.ExpressionVisitor.Visit(Expression exp) at Moq.Mock.AutoMockPropertiesVisitor.SetupMocks(Expression expression) at Moq.Mock.GetInterceptor(LambdaExpression lambda, Mock mock) at Moq.Mock.<>c__DisplayClass122.<Setup>b__11() at Moq.PexProtector.Invoke[T](Func1 function) at Moq.Mock.Setup1 expression) at Moq.Mock1.Setup%5BTResult%5D%28Expression`1 expression">T1,TResult Controllers\UserControllerTest.cs(29,0): at Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView()

+7  A: 

Rather than mocking the Session, one might consider setting up a different Configuration for unit-tests. This unit-testing Configuration uses a fast, in-process database like SQLite or Firebird. In the fixture setup, you create a new test database completely from scratch, run the scripts to set up the tables, and create a set of initial records. In the per-test setup, you open a transaction and in the post-test teardown, you rollback the transaction to restore the database to its previous state. In a sense, you are not mocking the Session, because that gets tricky, but you are mocking the actual database.

Justice
Thanks Justice. I had definitely thought of that and, if i cant get this working, it will be the way I go. But I'm trying to avoid the database altogether in this test project. If I can mock out NHibernate I feel I will have far more control over my tests...but thanks for the suggestion!
JP
Unfortunately, the NHibernate `Session` is very complicated when it comes to related objects, deferred loading, caching, and all the other things that NHibernate does. So I would just skip trying to mock it and instead try to mock the database. It's easy for NHibernate to generate a schema-creation script for any given database system from your mappings and then for you to execute that script to create an empty database with your schema on fixture-setup. From my own experience with NHibernate and from observing what frameworks like Rails do, this is essentially the only way to go.
Justice
hmm, you are right...altough it would be so nice it we don't need to use some mocked db...if we already have nhibernate which translate our relational model to object model, it is kind of strange to tested like that, we should be able to test it directly...nice question and answers, cheers :)
Marko
+5  A: 

Below is the solution I came up with which seems to work perfectly. Again, I am not testing NHibernate and I am not testing the database - I simply want to test the controllers which depend on NHibernate. The issue with the initial solution appears to be the fact that I was calling a Method as well as reading the List member of the session in the MOQ setup call. I broke up these calls by breaking the solution into a QueryMock and a Session Mock (create query returns an IQuery object). A transaction mock was also necessary as it is a dependency (in my case) of the session...

        [Test]
        public void DummyTest()
        {
            var userList = new List<User>() { new User() { ID = 2, FirstName = "John", LastName = "Peterson" } };
            var sessionMock = new Mock<ISession>();
            var queryMock = new Mock<IQuery>();
            var transactionMock = new Mock<ITransaction>();

            sessionMock.SetupGet(x => x.Transaction).Returns(transactionMock.Object);
            sessionMock.Setup(session => session.CreateQuery("from User")).Returns(queryMock.Object);
            queryMock.Setup(x => x.List<User>()).Returns(userList);

            var controller = new UsersController(sessionMock.Object);
            var result = controller.Index() as ViewResult;
            Assert.IsNotNull(result.ViewData);
        }
JP
A: 

Hey JP, were u able to mock the session. I am trying something similar but I get the type exception on Session Factory for not able to cast it to SessionFactoryImplementor.

Harry
+1  A: 

There is a very good article from Ayende, how you can test NHibernate:

http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx

cheers

Marko