2

I have an MVC app that uses NHibernate for ORM. Each controller takes an ISession construction parameter that is then used to perform CRUD operations on domain model objects. For example,

public class HomeController : Controller
{
    public HomeController(ISession session)
    {
        _session = session;
    }
    public ViewResult Index(DateTime minDate, DateTime maxDate)
    {
        var surveys = _session.CreateCriteria<Survey>()
                              .Add( Expression.Like("Name", "Sm%") )
                              .Add( Expression.Between("EntryDate", minDate, maxDate) )
                              .AddOrder( Order.Desc("EntryDate") )
                              .SetMaxResults(10)
                              .List<Survey>();

        // other logic that I want to unit test that does operations on the surveys variable
        return View(someObject);
    }
    private ISession _session;
}

I would like to unit test this controller in isolation, without actually hitting the database, by mocking the ISession object using Moq or RhinoMocks. However, it is going to be very difficult to mock the ISession interface in the unit test, because it is being used via a fluent interface that chains a number of calls together.

One alternative is to wrap the ISession usage via a repository pattern. I could write a wrapper class something like this:

public interface IRepository
{
   List<Survey> SearchSurveyByDate(DateTime minDate, DateTime maxDate);
}

public class SurveyRepository : IRepository
{
    public SurveyRepository(ISession session)
    {    
        _session = session;
    }
    public List<Survey> SearchSurveyByDate(DateTime minDate, DateTime maxDate)
    {
        return _session.CreateCriteria<Survey>()
                          .Add( Expression.Like("Name", "Sm%") )
                          .Add( Expression.Between("EntryDate", minDate, maxDate) )
                          .AddOrder( Order.Desc("EntryDate") )
                          .SetMaxResults(10)
                          .List<Survey>();
    }
    private ISession _session;
}

I could then re-write my controller to take an IRepository constructor argument, instead of an ISession argument:

public class HomeController : Controller
{
    public HomeController(IRepository repository)
    {
        _repository = repository;
    }

    public ViewResult Index(DateTime minDate, DateTime maxDate)
    {
        var surveys = _repository.SearchSurveyByDate(minDate, maxDate);
         // other logic that I want to unit test that does operations on the surveys variable
        return View(someObject);
    }
    private IRepository _repository;
}

This second approach would be much easier to unit test, because the IRepository interface would be much easier to mock than the ISession interface, since it is just a single method call. However, I really don't want to go down this route, because:

1) It seems like a really bad idea to create a whole new layer of abstraction and a lot more complexity just to make a unit test easier, and

2) There is a lot of commentary out there that rails against the idea of using a repository pattern with nHibernate, since the ISession interface is already a repository-like interface. (See especially Ayende's posts here and here) and I tend to agree with this commentary.

So my questions is, is there any way I can unit-test my initial implementation by mocking the ISession object? If not, is my only recourse to wrap the ISession query using the repository pattern, or is there some other way I can solve this?

Joe Alfano
  • 10,149
  • 6
  • 29
  • 40

3 Answers3

3

Oren tends to wander around a lot. He used to be a huge proponent of Repositories and Unit of Work. He will probably swing back around again to it, but with a different set of requirements.

Repository has some very specific advantages that none of Oren's comments have quite found solutions for. Also, what he recommends has it's own set of limitaitons and problems. Sometimes I feel like he's just exchanging one set of problems for another. It's also good when you need to provide different views of the same data, such as a Web Service, or Desktop application while still keeping the web app.

Having said that, he has a lot of good points. I'm just not sure there are good solutions for them yet.

Repository is still very useful for highly test driven scenarios. It's still useful if you don't know if you will stick with a given ORM or persistence layer and might want to swap it out with another one.

Oren's solution tends to couple nHimbernate more tightly into the app. That may not be a problem in many situations, in others it might be.

His approach of creating dedicated query classes is interesting, and is sort of a first step to CQRS, which might be a better total solution. But Software development is still so much more art or craft than science. We're still learning.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • Thanks for the comments. I can see pros and cons to using the repository pattern. Its use greatly simplifies unit testing. However, it is requires an additionally layer of abstraction, and it is so convenient and flexible to code directly against the ISession interface in your controllers. However, then your application is very tightly coupled to NHibernate. I guess there are no perfect solutions and everything is a trade-off, depending on what is most important to your app. – Joe Alfano May 27 '12 at 13:49
  • @JoeAlfano - There's noting wrong with another layer of abstraction per se. But, Oren's argument is that the typical Repository implementation adds more complexity, with little payoff. So a repository layer that did not add a lot of complexity might be a win, but i don't know what that would look like. – Erik Funkenbusch May 27 '12 at 16:55
2

Rather than mocking out ISession have you considered having your tests inherit from a base fixture that makes use of SQLite?

public class FixtureBase
{
    protected ISession Session { get; private set; }
    private static ISessionFactory _sessionFactory { get; set; }
    private static Configuration _configuration { get; set; }

    [SetUp]
    public void SetUp()
    {
        Session = SessionFactory.OpenSession();
        BuildSchema(Session);
    }

    private static ISessionFactory SessionFactory
    {
        get
        {
           if (_sessionFactory == null)
           {
                var cfg = Fluently.Configure()
                    .Database(FluentNHibernate.Cfg.Db.SQLiteConfiguration.Standard.ShowSql().InMemory())
                    .Mappings(configuration => configuration.FluentMappings.AddFromAssemblyOf<Residential>())
                    .ExposeConfiguration(c => _configuration = c);

                _sessionFactory = cfg.BuildSessionFactory();
           }

            return _sessionFactory;
        }
    }

    private static void BuildSchema(ISession session)
    {
        var export = new SchemaExport(_configuration);
        export.Execute(true, true, false, session.Connection, null);
    }

    [TearDown]
    public void TearDownContext()
    {
        Session.Close();
        Session.Dispose();
    }


}
Jesse
  • 8,223
  • 6
  • 49
  • 81
  • Thanks for your interesting suggestion. So the basic idea is to read your nHibernate configuration and use that information to construct the corresponding database schema in SqlLite using the SchemaExport method, for testing purposes. Would I also need to populate the SqlLite DB with appropriate sample data and delete the SQLLite file in the TestFixtureTearDown method after all tests are complete? Thanks. – Joe Alfano May 27 '12 at 13:57
  • @JoeAlfano As the database is in memory you could easily add "expected" or "sample data" to the database much as you would when setting up a mocked method / return value. In turns of deleting anything to do with SQLLite you shouldn't need to worry about that. – Jesse May 27 '12 at 16:21
2

Introducing repositories with named query methods does not add complexity to your system. Actually it reduces complexity and makes your code easier to understand and maintain. Compare original version:

public ViewResult Index(DateTime minDate, DateTime maxDate)
{
    var surveys = _session.CreateCriteria<Survey>()
                          .Add(Expression.Like("Name", "Sm%"))
                          .Add(Expression.Between("EntryDate", minDate, maxDate))
                          .AddOrder(Order.Desc("EntryDate"))
                          .SetMaxResults(10)
                          .List<Survey>();

     // other logic which operates on the surveys variable
     return View(someObject);
}

Frankly speaking all my memory slots where already occupied BEFORE I got to the actual logic of your method. It takes time for reader to understand which criteria you are building, what parameters are you passing and which values are returned. And I need to switch contexts between lines of code. I start thinking in terms of data access and Hibernate, then suddenly I'm back to the business logic level. And what if you have several places where you need to search surveys by date? Duplicate all this staff?

And now I'm reading version with repository:

public ViewResult Index(DateTime minDate, DateTime maxDate)
{
    var surveys = _repository.SearchSurveyByDate(minDate, maxDate);
    // other logic which operates on the surveys variable
    return View(someObject);
}

It takes me zero efforts to understand what happening here. This method has single responsibility and single level of abstraction. All data access related logic gone. Query logic is not duplicated in different places. Actually I don't care how it is implemented. Should I care at all, if main goal of this method is some other logic?

And, of course, you can write unit test for your business logic with no efforts (also if you are using TDD repository gives you ability to test your controller before you actually write data access logic, and when you will start writing repository implementation, you will have already designed repository interface):

[Test]
public void ShouldDoOtherLogic()
{
    // Arrange
    Mock<ISurveryRepository> repository = new Mock<ISurveryRepository>();
    repository.Setup(r => r.SearchSurveyByDate(minDate, maxDate))
              .Returns(surveys);

    // Act
    HomeController controller = new HomeController(repository.Object);
    ViewResult result = controller.Index(minDate, maxDate);

    // Assert
}

BTW In-memory database usage is good for acceptance testing, but for unit-testing I think its an overkill.

Also take a look at NHibernate Lambda Extensions or QueryOver in NHibernate 3.0 which use expressions to build criteria instead of strings. Your data access code will not break if you rename some field.

And also take a look on Range for passing pairs of min/max values.

Community
  • 1
  • 1
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • Thanks for the comments. I like your points about the cleaner separation of concerns and potential ability to re-use queries. I think you make a cogent case for using the repository pattern. Thanks to your comment and the other comments on my post, I think I can pretty clearly see both sides of the issues of wrapping an nHibernate ISession with a repository pattern. Thanks for the response. – Joe Alfano May 29 '12 at 01:53