Did you know? DZone has great portals for Python, Cloud, NoSQL, and HTML5!

Mike Hadlow is a Brighton, UK based developer, blogger and author of a number of open source frameworks and applications. Mike is a DZone MVB and is not an employee of DZone and has posted 27 posts at DZone. You can read more from them at their website. View Full User Profile

Testing NHibernate Mappings

04.03.2010
Email
Views: 2085
  • submit to reddit

I’m currently in the process of moving Suteki Shop from Linq-to-SQL to NHibernate. It turns out to be a bigger job than I anticipated, and I’ll be writing a post about my experiences soon. I’m using FNH (Fluent NHibernate) to build my mapping files rather than use the tedious built-in XML mapping. One of the big jobs I have is to write unit tests for all the mapping files. For each entity, I want to have a test that creates an instance of it, saves it to the database, and retrieves it. As you can imagine, it would be a tedious task to write all these. With that in mind I’ve created a simple NUnit test that can be reused for any mapped entity:

using System;
using System.Reflection;
using NUnit.Framework;
using Suteki.Common.Extensions;
using Suteki.Shop.Tests.Models.Builders;

namespace Suteki.Shop.Tests.Maps
{
/// <summary>
/// Runs a simple test for each entity listed. That it can be saved to the database and retrieved.
/// </summary>
[TestFixture]
public class SimpleMapTests : MapTestBase
{
[TestCase(typeof (PostZone))]
[TestCase(typeof (Postage))]
[TestCase(typeof (Country))]
public void SimpleMapTest(Type entityType)
{
var runSimpleMapTest = GetType().GetMethod("RunSimpleMapTest");
var runSimpleMapTestForType = runSimpleMapTest.MakeGenericMethod(entityType);
runSimpleMapTestForType.Invoke(this, new object[0]);
}

public void RunSimpleMapTest<TEntity>() where TEntity : class, new()
{
var id = 0;

InSession(session =>
{
var entity = GetDefaultsFor<TEntity>();

session.Save(entity);
id = (int) entity.GetPrimaryKey().Value;
});

InSession(session => session.Get<TEntity>(id));
}

static TEntity GetDefaultsFor<TEntity>()
{
var entityName = typeof (TEntity).Name;
var defaults = typeof(Default).GetMethod(entityName, BindingFlags.Static | BindingFlags.Public);
if (defaults == null)
{
Assert.Fail(string.Format("No static method Default.{0} found", entityName));
}
if (defaults.ReturnType != typeof (TEntity))
{
Assert.Fail(string.Format("Method Default.{0} does not have a return type of {0}", entityName));
}

return (TEntity) defaults.Invoke(null, new object[0]);
}
}
}

The core idea here is to use the NUnit TestCase attribute to run a test for each entity that I want to map. I have a generic test method ‘RunSimpleMapTest<TEntity>’ that gets called for each entity by ‘SimpleMapTest’. There’s a little bit of reflection voodoo to create and run a correctly typed ‘RunSimpleMapTest’, but the principle is simple.

RunSimpleMapTest gets a default instance of the entity class, saves it, grabs the generated id, and then retrieves the entity again using the id. This is enough to check that the basic map works and that I don’t have any non-virtual members in my entity. The ‘InSession’ method is just to save some typing, it looks like this:

protected void InSession(Action<ISession> action)
{
using (var session = InMemoryDatabaseManager.OpenSession())
using(var transaction = session.BeginTransaction())
{
action(session);
transaction.Commit();
}
}

The GetPrimaryKey method is an extension method that finds the primary key of any entity based on a simple convention; my primary keys are always called ‘Id’ or ‘<entity name>Id’.

I’m using an in-memory SQLite database for the tests that’s managed by the MapTestBase base class. This is such a common solution that I’ll just leave you to Google it rather than showing my implementation here.

The last thing to note, is where the actual entity instance comes from. The GetDefaultsFor<TEntity> method looks for a method with the same name as the entity in a class called ‘Default’. The default method for PostZone looks like this:

public static class Default
{
public static PostZone PostZone()
{
return new PostZone
{
Name = "UK",
Multiplier = 1.0M,
AskIfMaxWeight = false,
Position = 1,
IsActive = true,
FlatRate = 10M
};
}

.... lots of other similar methods
}

Now for each entity, I simply create the ClassMap, create a method to make a default entity in the Default class and add a new TestCase attribute to my test.

 

References
Published at DZone with permission of Mike Hadlow, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)