I have played with three different approaches to configuring Rhino.Commons, NHibernate and Windsor container for unit testing. I’ve spiked these three flavors in order to investigate building an infrastructure that is open to extension but closed to modification. I’m still in the process of doing my analysis but I now have 3 approaches to  play with.

My last blog entry showed how to use Rhino.Commons with an in memory SQLite database. The example also used Windsor container and it was my initial proof of concept. There are lots of blogs like this but I still had trouble figuring out how to make this rather easy approach work.

Since then I have been refactoring this approach and making it more closely follow the Open-Closed Principle first coined in 1988 by Bertrand Meyer in his book, Object-Oriented Software Construction. Prentice Hall. ISBN 0136290493. There is a very good article on the ObjectMentor blog about the OCP.

The first refactor I did was move away from using the hibernate.cfg.xml file and do it NHibernate configuration dynamically. I did this by creating a ConventionConfiguration class that uses NHibernate.Cfg.Environment static class properties.

using NHibernate;
using Rhino.Commons;
using Configuration = NHibernate.Cfg.Configuration;
using Environment=NHibernate.Cfg.Environment;

namespace CrawBuck.Commons.NHibernate {
    public class ConventionConfiguration : Configuration {
        private readonly Configuration configuration = new Configuration();

        public Configuration getConfiguration() { return configuration; }

        /// <summary>
        /// This is the wiring up point for NHibernate. This sets up the Environment properties for
        /// SQLite in memory database
        /// </summary>
        public void ConfigureNHiberateForSQLite() {
            IDictionary<string, string> properties = new Dictionary<string, string>();
            properties.Add(Environment.ConnectionProvider, "NHibernate.Connection.DriverConnectionProvider");
            properties.Add(Environment.ConnectionDriver, "NHibernate.Driver.SQLite20Driver");
            properties.Add(Environment.ConnectionString, "Data Source=:memory:;Version=3;New=True;");
            properties.Add(Environment.ShowSql, "true");
            properties.Add(Environment.Dialect, "NHibernate.Dialect.SQLiteDialect");
            properties.Add(Environment.ReleaseConnections, "on_close");
            properties.Add(Environment.ProxyFactoryFactoryClass, "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");
            configuration.SetProperties(properties);
        }
    }
}

This class will only do two things right now. It will dynamically configure NHibernate to use SQLite and it will return the NHibernate.Cfg.Configuration variable that holds these properties. For my infrastructure work I will later add a method to dynamically configure MS SQL Server database. Unit testing should be fast but there are times when you actually have to hit a real database. I call real database testing ‘integration database testing’ and don’t have my continuous integration build machine run this type of testing at check in. I do want to do integration database testing but I set the build for this to happen at about 4 hour intervals. Currently I’m using CruiseControl.NET but I’m going to be moving to Hudson.

The next change I made was to move the configuration of the Windsor container to a class of its own, BinsorlessUnitOfWorkApplication. This is an extension of Rhino.Commons.HttpModules.UnitOfWorkApplication.

using System;
using Castle.Facilities.FactorySupport;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using NHibernate;
using NHibernate.Cfg;
using Rhino.Commons;
using Rhino.Commons.HttpModules;
using Environment=System.Environment;

namespace CrawBuck.Commons.HttpModules {
    public class BinsorlessUnitOfWorkApplication : UnitOfWorkApplication {
        protected override IWindsorContainer CreateContainer(string windsorConfig) {
            //ignore windsor config string and create it empty…
            return new RhinoContainer();
        }

        /// <summary>
        /// This wires up the IoC Rhino/Windsor container for the basic facilities and components.
        /// </summary>
        public virtual void InitializeContainer() {
            Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
            IoC.Initialize(new WindsorContainer());
            IoC.Container
                .AddFacility<FactorySupportFacility>("factory.support")
                .AddFacility<RhinoTransactionFacility>("transaction")
                .Register(
                Component.For(typeof(IRepository<>)).ImplementedBy(typeof(NHRepository<>)),
                Component.For<IUnitOfWorkFactory>().ImplementedBy<NHibernateUnitOfWorkFactory>());
        }

        public void RegisterSessionFactory(Configuration configuration) {
            (IoC.Resolve<IUnitOfWorkFactory>() as NHibernateUnitOfWorkFactory).RegisterSessionFactory(configuration, configuration.BuildSessionFactory());
        }

        public ISessionFactory GetUnitOfWorkFactory() {
            return (IoC.Resolve<IUnitOfWorkFactory>() as NHibernateUnitOfWorkFactory).NHibernateSessionFactory;
        }
    }
}

I had to override the CreateContainer method in Rhino.Commons.HttpModules.UnitOfWork class to remove the need for the windsor.boo file. Once these two classes were in place I refactored the code from my last blog.

using System;
using System.Reflection;
using CrawBuck.Commons.HttpModules;
using CrawBuck.Commons.NHibernate;
using CrawBuck.Commons.Tests.Repository.Domain;
using NHibernate.Criterion;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
using Rhino.Commons;

namespace CrawBuck.Commons.Tests.Repository {
    [TestFixture]
    public class RhinoCommonsUnitOfWork {
        protected BinsorlessUnitOfWorkApplication unitOfWorkApplication = new BinsorlessUnitOfWorkApplication();
        protected ConventionConfiguration cc = new ConventionConfiguration();

        [SetUp]
        public void SetUp() {
            unitOfWorkApplication.InitializeContainer();
            cc.ConfigureNHiberateForSQLite();
            cc.getConfiguration().AddAssembly(Assembly.GetExecutingAssembly());
            unitOfWorkApplication.RegisterSessionFactory(cc.getConfiguration());
        }

        [Test]
        public void CanStartUnitOfWorkTest() {
            Customer customer = new Customer("Alan", "Buck");
            Guid guid = customer.Id;
            using(IUnitOfWork uow = UnitOfWork.Start()) {
                uow.BeginTransaction();
                new SchemaExport(cc.getConfiguration()).Execute(false, true, false, true, UnitOfWork.CurrentSession.Connection, null);
                Repository<Customer>.Save(customer);
                uow.TransactionalFlush();
                Customer loadCustomer = Repository<Customer>.Load(guid);
                Assert.AreEqual(loadCustomer.FirstName, "Alan");
                Assert.AreEqual(loadCustomer.LastName, "Buck");
                DetachedCriteria criteria = DetachedCriteria.For(typeof(Customer)).Add(Restrictions.Eq("Id", guid));
                Customer repositoryCustomer = Repository<Customer>.FindOne(criteria);
                Assert.AreEqual(repositoryCustomer.FirstName, "Alan");
                Assert.AreEqual(repositoryCustomer.LastName, "Buck");
            }
        }
    }
}

The following tests use either the Customer or Person classes.

using System;

namespace CrawBuck.Commons.Tests.Repository.Domain {
    public class Customer {
        private Guid id = Guid.NewGuid();
        private string firstName;
        private string lastName;

        public Customer() { }

        public Customer(string firstName, string lastName)
        {
            this.firstName = firstName;
            this.lastName = lastName;
        }
        public virtual Guid Id { get { return id; } set { id = value; } }
        public virtual string FirstName { get { return firstName; } set { firstName = value; } }
        public virtual string LastName { get { return lastName; } set { lastName = value; } }
    }
}
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-lazy=‘false’
    assembly=‘CrawBuck.Commons.Tests’
    namespace=‘CrawBuck.Commons.Tests.Repository.Domain’
    xmlns=‘urn:nhibernate-mapping-2.2′>
  <class name=‘Customer’>
    <id name="Id" column="Id">
      <generator class="assigned"/>
    </id>
    <property name="FirstName" />
    <property name="LastName"/>
  </class>
</hibernate-mapping>
using System;

namespace CrawBuck.Commons.Tests.Repository.Domain {
    public class Person {
        private Guid id = Guid.NewGuid();
        private string firstName;
        private string lastName;
        private int age;

        public Person() { }

        public Person(string firstName, string lastName, int age)
        {
            this.firstName = firstName;
            this.lastName = lastName;
            this.age = age;
        }

        public virtual Guid Id { get { return id; } set { id = value; } }
        public virtual string FirstName { get { return firstName; } set { firstName = value; } }
        public virtual string LastName { get { return lastName; } set { lastName = value; } }
        public virtual int Age { get { return age; } set { age = value; } }
    }
}
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-lazy=‘false’
    assembly=‘CrawBuck.Commons.Tests’
    namespace=‘CrawBuck.Commons.Tests.Repository.Domain’
    xmlns=‘urn:nhibernate-mapping-2.2′>
  <class name=‘Person’>
    <id name="Id" column="Id">
      <generator class="assigned"/>
    </id>
    <property name="FirstName" />
    <property name="LastName"/>
    <property name="Age" type="int"/>
  </class>
</hibernate-mapping>

The next refactor was to create a test base class that will allow the configuration of both the Windsor container and NHibernate using SQLite to be used by any unit test class.

using System;
using System.Reflection;
using CrawBuck.Commons.HttpModules;
using CrawBuck.Commons.NHibernate;
using CrawBuck.Commons.Tests.Repository.Domain;
using NHibernate.Criterion;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
using Rhino.Commons;

namespace CrawBuck.Commons.Tests.Repository {
    [TestFixture]
    public class RhinoCommonsUnitOfWork {
        protected BinsorlessUnitOfWorkApplication unitOfWorkApplication = new BinsorlessUnitOfWorkApplication();
        protected ConventionConfiguration cc = new ConventionConfiguration();

        [SetUp]
        public void SetUp() {
            unitOfWorkApplication.InitializeContainer();
            cc.ConfigureNHiberateForSQLite();
            cc.getConfiguration().AddAssembly(Assembly.GetExecutingAssembly());
            unitOfWorkApplication.RegisterSessionFactory(cc.getConfiguration());
        }

        [Test]
        public void CanStartUnitOfWorkTest() {
            Customer customer = new Customer("Alan", "Buck");
            Guid guid = customer.Id;
            using(IUnitOfWork uow = UnitOfWork.Start()) {
                uow.BeginTransaction();
                new SchemaExport(cc.getConfiguration()).Execute(false, true, false, true, UnitOfWork.CurrentSession.Connection, null);
                Repository<Customer>.Save(customer);
                uow.TransactionalFlush();
                Customer loadCustomer = Repository<Customer>.Load(guid);
                Assert.AreEqual(loadCustomer.FirstName, "Alan");
                Assert.AreEqual(loadCustomer.LastName, "Buck");
                DetachedCriteria criteria = DetachedCriteria.For(typeof(Customer)).Add(Restrictions.Eq("Id", guid));
                Customer repositoryCustomer = Repository<Customer>.FindOne(criteria);
                Assert.AreEqual(repositoryCustomer.FirstName, "Alan");
                Assert.AreEqual(repositoryCustomer.LastName, "Buck");
            }
        }
    }
}

Here is a unit test class that takes advantage of the RepositoryTestsBase class.

using System;
using CrawBuck.Commons.Tests.ForTesting;
using CrawBuck.Commons.Tests.Repository.Domain;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
using Rhino.Commons;

namespace CrawBuck.Commons.Tests.Repository {
    [TestFixture]
    public class RepositoryTest : RepositoryTestsBase {
        private ISession session;
        private Person person;
        private Guid guid;

        [TestFixtureSetUp]
        public void TestFixtureSetUp() {
            OneTimeTestInitialize();
        }

        [SetUp]
        public void SetUp() {
            person = new Person("Alan", "Buck", 62);
            guid = person.Id;
        }

        [Test]
        public void ShouldCreateRepositoryTest() {
            session = CreateSession();
            session.Save(person);
            session.Flush();
            session.Clear();
            Person loadPerson = session.Load<Person>(guid);
            Assert.AreEqual(loadPerson.FirstName, "Alan");
            Assert.AreEqual(loadPerson.LastName, "Buck");
            Assert.AreEqual(loadPerson.Age, 62);
            session.Dispose();
        }

        [Test]
        public void ShouldCreateUsingIRepositoryTest() {
            using (IUnitOfWork uow = UnitOfWork.Start()) {
                new SchemaExport(configuration).Execute(false, true, false, true, UnitOfWork.CurrentSession.Connection, null);
                Repository<Person>.Save(person);
                uow.TransactionalFlush();
                DetachedCriteria criteria = DetachedCriteria.For(typeof(Person)).Add(Restrictions.Eq("Id", guid));
                Person loadPerson = Repository<Person>.FindOne(criteria);
                Assert.AreEqual(loadPerson.FirstName, "Alan");
                Assert.AreEqual(loadPerson.LastName, "Buck");
                Assert.AreEqual(loadPerson.Age, 62);
            }
        }

        [TearDown]
        public void TearDown() {}
    }
}

That covers two of the approaches to using Rhino.Commons, NHibernate and the Windsor container. The last approach is to use FluentNHibernate. I’m not using the ClassMap of FluentNHibernate. This still uses the .hbm.xml files.

using System;
using System.IO;
using System.Reflection;
using Castle.Windsor;
using CrawBuck.Commons.HttpModules;
using CrawBuck.Commons.NHibernate;
using CrawBuck.Commons.Tests.Repository.Domain;
using FluentNHibernate.AutoMap;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
using Rhino.Commons;
using Configuration = NHibernate.Cfg.Configuration;
using Environment=NHibernate.Cfg.Environment;

namespace CrawBuck.Commons.Tests.Repository {
    [TestFixture]
    public class FluentNHibernate {
        private readonly BinsorlessUnitOfWorkApplication unitOfWorkApplication = new BinsorlessUnitOfWorkApplication();
        private Configuration config;
        private readonly ConventionConfiguration cc = new ConventionConfiguration();

        [SetUp]
        public void SetUp() {
            unitOfWorkApplication.InitializeContainer();
            cc.ConfigureNHiberateForSQLite();
            config = cc.getConfiguration();
            config.AddAssembly(Assembly.GetExecutingAssembly());
            CreateRhinoSessionFactoryForSQLite();
        }

        [Test]
        public void CanStartUnitOfWorkTest() {
            Customer customer = new Customer("Alan", "Buck");
            Guid guid = customer.Id;
            using (IUnitOfWork uow = UnitOfWork.Start()) {
                uow.BeginTransaction();
                new SchemaExport(config).Execute(false, true, false, true, UnitOfWork.CurrentSession.Connection, null);
                Repository<Customer>.Save(customer);
                uow.TransactionalFlush();
                Customer loadCustomer = Repository<Customer>.Load(guid);
                Assert.AreEqual(loadCustomer.FirstName, "Alan");
                Assert.AreEqual(loadCustomer.LastName, "Buck");
                DetachedCriteria criteria = DetachedCriteria.For(typeof(Customer)).Add(Restrictions.Eq("Id", guid));
                Customer repositoryCustomer = Repository<Customer>.FindOne(criteria);
                Assert.AreEqual(repositoryCustomer.FirstName, "Alan");
                Assert.AreEqual(repositoryCustomer.LastName, "Buck");
            }
        }

        private void CreateRhinoSessionFactoryForSQLite() {
            IPersistenceConfigurer configurer = SQLiteConfiguration.Standard.InMemory().ShowSql();
            ISessionFactory sessionFactory = Fluently.Configure(config).Database(configurer).Mappings(m => m.FluentMappings.AddFromAssemblyOf<Customer>()).BuildSessionFactory();
            (Rhino.Commons.IoC.Resolve<IUnitOfWorkFactory>() as NHibernateUnitOfWorkFactory).RegisterSessionFactory(config, sessionFactory);
        }
    }
}

You will notice that I have a CreateRhinoSessionFactoryForSQLite method in this test class. It’s there because I’m lazy and it’s late. Maybe in a future blog I will refactor this unit test to use ClassMap and move the CreateRhinoSessionFactoryForSQLite to the BinsorlessUnitOfWorkApplication class. I’m still moving up the learning curve on FluentNHibernate and have only the basic understanding of what all this OSS project has to offer.