Testing CRUD Operations With NHibernate

13 commentsWritten on December 7th, 2009 by
Categories: NHibernate, testing

I was asked to show how you can easily do CRUD tests, so here’s a base class that makes it very easy

    public abstract class CrudTest<TEntity, TId> : NHibernateTest

        where TEntity : IHaveAnId<TId>

    {

        [Test]

        public virtual void SelectQueryWorks()

        {

            session.CreateCriteria(typeof(TEntity)).SetMaxResults(5).List();

        }

 

        [Test]

        public virtual void AddEntity_EntityWasAdded()

        {

            var entity = BuildEntity();

 

            InsertEntity(entity);

 

            session.Evict(entity);

 

            var reloadedEntity = session.Get<TEntity>(entity.Id);

 

            Assert.IsNotNull(reloadedEntity);

            AssertAreEqual(entity, reloadedEntity);

            AssertValidId(reloadedEntity);

        }

 

        [Test]

        public virtual void UpdateEntity_EntityWasUpdated()

        {

            var entity = BuildEntity();

 

            InsertEntity(entity);

            ModifyEntity(entity);

            UpdateEntity(entity);

 

            session.Evict(entity);

 

            var reloadedEntity = session.Get<TEntity>(entity.Id);

            Assert.IsNotNull(reloadedEntity);

            AssertAreEqual(entity, reloadedEntity);

        }

 

        [Test]

        public virtual void DeleteEntity_EntityWasDeleted()

        {

            var entity = BuildEntity();

 

            InsertEntity(entity);

            DeleteEntity(entity);

 

            Assert.IsNull(session.Get<TEntity>(entity.Id));

        }

 

        protected virtual void InsertEntity(TEntity entity)

        {

            session.Save(entity);

            session.Flush();

        }

 

        protected virtual void UpdateEntity(TEntity entity)

        {

            session.Update(entity);

            session.Flush();

        }

 

        protected virtual void DeleteEntity(TEntity entity)

        {

            session.Delete(entity);

            session.Flush();

        }

 

        protected abstract TEntity BuildEntity();

        protected abstract void ModifyEntity(TEntity entity);

        protected abstract void AssertAreEqual(TEntity expectedEntity, TEntity actualEntity);

        protected abstract void AssertValidId(TEntity entity);

    }

 

Simply inherit from this class, implement the BuildEntity, ModifyEntity, AssertAreEqual and AssertValidId methods and that’s it.  Those methods are usually pretty simple.  In BuildEntity you just create an unpersisted entity and assign values to the properties, in ModifyEntity you modify the properties, and in AssertAreEqual you compare the properties of both instances.  In AssertValidId, you make sure that the ID value is ok (depending on your identifier strategy).

This is good for regular CRUD operations, though we typically add extra tests when we want to test cascades or one-to-many associations mapped with inverse=”true”.

  • http://grantpalin.com Grant Palin

    Just came from your previous post. This is a nice follow-up, another worthy of bookmarking!

    Supposing that you follow the multi-layer approach to your project, this code would be used in testing persistence with NHibernate, right? So higher-level code which would normally interact with NH-backed repositories would actually interact with fake version of such, just so you can keep the different test types separate…Am I right??

  • http://davybrion.com Davy Brion

    @Grant

    the class shown in this post is only useful to test the correctness of your mappings by making sure that CRUD operations work for all entities. and as mentioned in the previous post, we also write tests for each specific custom query that we write.

    our higher level code (the one that indees communicates with repositories for all NH-related tasks) is always tested with mocked repositories.

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

  • Pingback: Ere Thrice the Sun Done Salutation to the Dawn « Search And Destroy

  • Morten Jacobsen

    Great article, I’m trying to incorporate your ideas into my own project.. I have two questions though (this may seem stupid, but I’m still new to TDD and I’m sorta forcing myself to write tests and learn it properly):

    Say you have a CustomerTests class that inherits your baseclass.. Because customers have orders we need to test add/remove orders methods.. Would you then override the buildEntity etc. to also create orders so you can manipulate those or would you have the order creation logic separate (i.e. call BuildEntity() to get a customer, then create and add order to it)?

    Also, when you reload your customer, how much detail would you go into to check that the order was there? Would you simply check that order count has increased by one or would you check the details of the order also?

    Regards,
    Morten

  • http://davybrion.com Davy Brion

    @Morten

    i would test the add/remove methods in separate test methods and leave the base methods in this test fixture stricture for the entity itself, and many-to-ones

    but one-to-many’s or many-to-many’s deserver their own test methods :)

    as for what to check on => i’d go for the count plus an ID check if you also have specific tests for the orders, or if you don’t have those, i’d do a complete check

  • Morten Jacobsen

    Davy,

    I’ve slightly modified your baseclasses, but whenever i try to run the tests, i get the following exception:

    TypeInitializationException: The type initializer for ‘NHibernate.ByteCode.Castle.ProxyFactory’ threw an exception. Could not load file or assembly ‘Castle.DynamicProxy2, Version=2.1.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0×80131040)

    Any ideas?

    My changes mainly concerns the use of ISession directly instead of the ActiveSessionManager and UnitOfWork.. Also, I have a TestFixtureSetUp method that configures NHibernate and builds the sessionfactory (it is here i get the exception)..

  • Morten Jacobsen

    I forgot to point out that the assemblies are indeed referenced in the project and i have tried readding them to no avail..

  • http://davybrion.com Davy Brion

    @Morten

    did you include a reference to Castle.DynamicProxy2 in your test project?

  • http://davybrion.com Davy Brion

    @Morten

    sorry, missed the last comment

    did you also include Castle.Core? are you sure that you have the correct versions? maybe your NHibernate.ByteCode.Castle.ProxyFactory was built against a different version of DynamicProxy2 than the one you included.

  • Morten Jacobsen

    Davy,

    Yes i’ve tried with and without Castle.Core.. How do I check which version nhibernate requires? I downloaded the lastest DynamicProxy2 and the lastest Nhibernate releases..

  • http://davybrion.com Davy Brion

    @Morten

    your NHibernate assemblies were compiled against Castle 2.1.0.0 (you can tell from the version number listed in the exception message)

  • Morten Jacobsen

    Aha, I was misinterpreting the error message.. It seems the dll I have is DynamicProxy2 2.1.1.. Two months of vacation seems to have affected my thinking :) .. Anyways, thanks for the help :)