Unit Testing An NHibernate Application

12 commentsWritten on December 6th, 2009 by
Categories: NHibernate, testing

Grant Palin recently asked me for an in-depth article on TDD’ing an NHibernate application. While this post won’t be very in-depth, it might be helpful already.  There are basically two approaches that i’ve seen used with good results, though there are obviously more approaches that you can use.  I’m going to limit the scope of this post to the following two approaches though, and i’ll also discuss exactly what we test.

Creating a new database for each test (or testfixture)

This approach creates the database at the beginning of each test (or testfixture), runs the test (or tests in the fixture), and then destroys the database after the test (or testfixture) completed.  The easiest way to do this is to create a base test class that all of you data access test class should inherit from.  Here’s a simple example:

    public class NHibernateTest

    {

        private Configuration configuration;

        protected ISessionFactory sessionFactory;

 

        [TestFixtureSetUp]

        public void TestFixtureSetup()

        {

            configuration = new Configuration()

                .Configure()

                .AddAssembly("YourAssemblyName");

 

            new SchemaExport(configuration).Create(false, true);

            sessionFactory = configuration.BuildSessionFactory();

        }

 

        [TestFixtureTearDown]

        public void TestFixtureTearDown()

        {

            new SchemaExport(configuration).Drop(false, true);

        }

 

        protected ISession CreateTransactionalSession()

        {

            var session = sessionFactory.OpenSession();

            session.BeginTransaction();

            return session;

        }

    }

 

This class will create the database from scratch once for each TestFixture, which means that each test in the fixture will use the same database.  It also destroys the database at the end of the fixture.  It creates the database based on your mappings, and as you can see, you really don’t have to do a lot for this.  If you want the database to be recreated and dropped for each test, then you obviously need to move the code in the TestFixtureSetup and TestFixtureTearDown methods to your regular SetUp and TearDown methods.  If you go that route, i’d advise you to include empty template methods before and after the setup and teardown so you can plug in some extra code before and after these operations in your derived test classes.

The biggest benefit of this approach is that you don’t have any possibly present state in the database that can influence your tests.  The downside is that you can’t rely on certain data (eg reference data) to be present and you have to recreate it whenever you need it.   You can also use multiple transactions in your tests, though you are also responsible for cleaning any data that is left in the database at the end of each test.  You also need to guarantee that this always happens because any data that is left by one test might influence another one.   Also, if you leave data in the database, that might lead to problems when dropping the database, so you really need to be careful with this.

Another problem with this example is that there is no automatic way to push the ISession instance to your data access components.  That could easily be added though, depending on how your data access components retrieve a reference to a valid ISession.

Tests that automatically roll back their transactions

This is the approach that we always use at work.  These tests require your database to be set up in a valid manner before your tests begin.  Each test uses one transaction, which is automatically rolled back at the end of the test to prevent the possibility of any test data remaining in the database.   With this way of testing, it’s also possible to provide some kind of ‘known state’ in the database (eg reference data) that you can use from within your code.

Here’s the NHibernateTest class that our NHibernate test classes all inherit from:

    public abstract class NHibernateTest

    {

        protected static readonly ActiveSessionManager activeSessionManager = new ActiveSessionManager();

 

        private UnitOfWork unitOfWork;

        protected ISession session;

 

        protected abstract ISessionProvider GetSessionProvider();

 

        protected virtual IActiveSessionManager GetActiveSessionManager()

        {

            return activeSessionManager;

        }

 

        [SetUp]

        public void SetUp()

        {

            BeforeSetup();

            var sessionManager = GetActiveSessionManager();

            unitOfWork = new UnitOfWork(GetSessionProvider(), sessionManager);

            session = sessionManager.GetActiveSession();

            unitOfWork.CreateTransaction();

            AfterSetup();

        }

 

        [TearDown]

        public void TearDown()

        {

            BeforeTearDown();

 

            if (unitOfWork != null)

            {

                unitOfWork.Dispose();

            }

 

            unitOfWork = null;

            AfterTearDown();

        }

 

        protected virtual void BeforeSetup() { }

        protected virtual void AfterSetup() { }

        protected virtual void BeforeTearDown() { }

        protected virtual void AfterTearDown() { }

    }

 

Now, this example uses our UnitOfWork and ActiveSessionManager classes (read this if you haven’t seen those yet) to make sure that our data access components can access the current ISession instance.  Each test has a valid ISession present, which has already created a transaction and we can create/modify/delete data, run our queries, modify some stuff again, run our queries again and perform our assertions, all in the same transaction.  After the test is completed, the transaction is never committed (and thus, automatically rolled back) so none of that data ever remains in the database.

What Exactly Do We Test?

Well, we test everything basically.  We test all of our CRUD operations (again, with a simple base class which only requires you to implement the BuildEntity, ModifyEntity and AssertEqual methods and does all of the operations and checks automatically) for each entity.  That’s right, for each entity.  The extra work that this requires really doesn’t take a lot of time and it lets us know for a fact that our mappings are valid.

We also test every custom query that we write, always using the following approach: create some test data, flush the session, perform your query and verify that the expected data has indeed been returned by the query, and also that data that shouldn’t have been returned isn’t there.

And that’s pretty much it.  We mock the data layer in all of our other tests, so our CRUD and query tests are the only ones that actually use NHibernate and the database.  But CRUD actions and queries are tested very extensively.

  • Sean Gough

    Wow, your timing couldn’t be better as I just started experimenting with this last week. So thanks a ton for posting this! I agree that testing CRUD ops is key, but it always seems messy when I actually try to do it thoroughly. Can you shed some light on how you test each entity automatically?

  • http://davybrion.com Davy Brion

    @Sean,

    i’ll post the base class for the CRUD tests later today

  • Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #492

  • http://www.watchfinder.co.uk Jonathan Adams

    Great post Davy, one point, these are Integration tests not unit tests and should be run away from your unit tests.

    But great post !

  • Joe

    Cool,

    I’ve been using MBUnit for testing and this has a nice little attribute [RollBack] that will rollback any transactions on the database.

    Thanks, Joe

  • Renana

    Great post, great timing.

    I just entered the world of unit testing and ActiveRecords. I was writing test with the “new database” scenario and will switch to the “rollback” scenario which will suit me more since I prefer to do my testings on a bit more complex database.

    Thanks,
    Renana

  • http://grantpalin.com Grant Palin

    So, so grateful. :) Straight to the bookmarks.

    This post fills an important piece of the NHibernate puzzle that I am currently working on. I’ve known that it is important to test as much of your code as possible, but I didn’t know how you would do it with NH involved. It seems clear to me that integration tests, rather than unit tests, are appropriate in this situation, and the transaction-rollback technique makes more sense.

    There are other pieces in the NH puzzle that have yet to be filled, but this important one is done now. I’ll be referring back to this post – and your other posts on NH – once my new project gets a little further along.

  • Pingback: Weekly Links #83 | GrantPalin.com

  • RT

    Hi,

    I was scratching my head over something in your posts here..BTW, great posts…

    You instantiate ActiveSessionManager in this class without any parameters. On the other hand, ActiveSessionManager has a single construction with IRequestState as a parameter. So, the code will not compile. Am I missing something?

    Thanks.

  • RT

    Just following up on my earlier post…

    First things first…It should be constructor instead of construction in my previous post.

    Second, I’ll try to answer my own question…The ActiveSessionManager should be implemented without a IRequestState paramater because in a Unit testing environment there is no thread safety issue and there is nothing sysnonymous to HttpContext.Current.Items as in an ASP.net environment. We still need to store the session somewhere; so, we can store it just a private variable in ActiveSessionManager and modify the Current property accordingly.

    Am I right?

    I’m trying to learn this technology without a mentor.

    Thanks

  • http://davybrion.com Davy Brion

    @RT

    you’re right on all counts :)

  • A1104201