Unit Testing With NHibernate And SQLite

Good unit tests should, among other things, be easily repeatable and independent. If you're testing against a full blown MySQL or MSSQL server it can become difficult to easily repeat a test if you rely on certain records holding certain Ids, and the independent nature of the test is impacted as it's not well isolated from a specific server.

Testing against an SQLite database can help alleviate these issues as the flatfile database can be created and thrown away after each test, making it easy to repeat a test on fresh data each time.

So let's look at how to go about setting this up.

The Standard SessionFactory

In order to get an NHibernate session normally in my application, I use a provider class to provide access to a SessionFactory:
public static class SessionFactoryProvider
{
private static ISessionFactory sessionFactory;
private static Object lockObj = new Object();

private static void BuildSessionFactory()
{
string connStr = ConfigurationManager.ConnectionStrings["SqlServer"].ConnectionString;

sessionFactory = Fluently.Configure().
Database(MsSqlConfiguration.MsSql2005.ConnectionString(connStr)).
Mappings(m => m.FluentMappings.AddFromAssemblyOf()).
BuildSessionFactory();
}

public static ISession OpenSession()
{
if (sessionFactory == null)
lock(lockObj)
if (sessionFactory == null)
BuildSessionFactory();

return sessionFactory.OpenSession();
}
}

This will normally live in the assembly that contains all the data access logic, and will used through dependency injection.

The Test SessionFactory

To get an SQLite database for testing, we need to create it in a slightly different way, specifying a filename for SQLite to use, and providing a way to tear it down after use:
namespace DITests
{
public static class TestSessionFactoryProvider
{
private static ISessionFactory sessionFactory;
private static string dbFile;

private static string GetDbFileName()
{
var path = Path.GetFullPath(Path.GetRandomFileName() + ".Test.db");
if (File.Exists(path))
{
File.Delete(path);
}
return path;
}

private static void BuildSessionFactory()
{
dbFile = GetDbFileName();

sessionFactory = Fluently.Configure().
Database(SQLiteConfiguration.Standard.UsingFile(dbFile).ShowSql()).
ExposeConfiguration(c =>
{
c.Properties.Add("hbm2ddl.keywords", "none");
c.Properties.Add("hbm2ddl.auto", "create");
}).
Mappings(m => m.FluentMappings.AddFromAssemblyOf()).
BuildSessionFactory();
}

public static ISession OpenSession()
{
if (sessionFactory == null) BuildSessionFactory();
return sessionFactory.OpenSession();
}

public static void Teardown()
{
if (sessionFactory != null)
{
sessionFactory.Dispose();
sessionFactory = null;
}

if (File.Exists(dbFile))
{
File.Delete(dbFile);
}
}
}
}

Something To Test

Now that the test session factory is setup, we need something to test, so here's a very simple user class, mapping and repository:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate;
using NHibernate.Linq;

namespace MVC3DI.Models
{
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}

public class UserMap : ClassMap
{
public UserMap()
{
Id(c => c.Id);
Map(c => c.Name);
Map(c => c.Age);
}
}
public class UserRepository
{
ISession session;
private IQueryable Users
{
get { return session.Query(); }
}

public UserRepository(ISession session)
{
this.session = session;
}

public IList GetUsers()
{
using (session.BeginTransaction())
{
var query = from c in Users select c;
return query.ToList();
}
}

public void Save(User user)
{
using (ITransaction t = session.BeginTransaction())
{
session.Save(user);
t.Commit();
}
}
}
}

The Test

And finally, we can setup some tests to run. Because the user repository takes an ISession in its constructor, we can easily change which SessionProviderFactory we're using, allowing us to drop in our test one in order to test against SQLite:
using System;
using System.Collections.Generic;
using NUnit.Framework;
using MVC3DI.Models;

namespace DITests
{
[TestFixture]
public class HireContractDALTests
{
UserRepository repo;

[SetUp]
public void Setup()
{
repo = new UserRepository(TestSessionFactoryProvider.OpenSession());

//insert some test checks
var users = new List(){
new User(){ Name = "Frank", Age = 24 },
new User(){ Name = "Bob", Age = 22 },
new User(){ Name = "Bert", Age = 30 },
};
users.ForEach(p => repo.Save(p));
}

[TearDown]
public void TearDown()
{
TestSessionFactoryProvider.Teardown();
}

[Test]
public void TestTryLoadUsers()
{
var users = repo.GetUsers();
Assert.AreEqual(3, users.Count);
Assert.AreEqual("Frank", users[0].Name);
Assert.AreEqual("Bob", users[1].Name);
Assert.AreEqual("Bert", users[2].Name);
}
}
}

This is a pretty straight forward test which fires up the database, creates a few records, and then reads them back in order to verify that they were saved correctly.

Limitations

The big limitation with this method is that you can only test code that is database agnostic. This should be fine if you're only strictly accessing records through NHibernate as that will take care of creating the correct SQL for you, but if you write any SQL yourself with keywords specific to a certain provider, it will quickly fall over when run against SQLite.

There's not a whole lot that can be done about this; you could extract the database specific functions to a more specific implementation, but then that would need testing too, which you can only do against the database it's targeted at, so that seems a little pointless. Which leaves you with either not testing that code or not writing it in the first place!

Summary

There is some debate as to the value of testing at the database level, especially when you're not testing against the database that will be used in production, but personally I think it can still be useful in verifying that mappings are working correctly and to test any basic logic that may live down at that level.

Using SQLite as a replacement during testing ensures your tests will run quickly and be independant of any outside requirements. Whilst the downside to this is that you can't test vendor specific SQL with this method, it's generally best to avoid this anyway when using an ORM such as NHibernate.

Unit Testing A Threaded Service Class

Having become intrigued by Test Driven Development, I've spent a good deal of time getting to grips with the tools and techniques, but find I'm still coming accross scenarios that leave me wondering, "How on earth do I test this?".

One of the biggest areas that's given me grief is multi-threading. I spent some of today trying to decide the best way to test a worker class that does its work in a secondary thread and returns its completion status asynchronously.

The application structure generally looks like this:

MainForm <-> App <-> Worker Class

The GUI may invoke theĀ  Worker Class to do some work that takes a significant amount of time, so this is obviously threaded.

This forms a pretty standard MVC structure, so my initial plan was to use the MVC structure to feed back completion status through the App as would happen in a standard MVC model, treating the app as the controller, without the use of any events. I didn't want to make use of an event here as I felt it would be introducing additional complexity when deciding where the event should be hooked and so on.

Whilst this felt like the simplest approach, it fell flat on not being very testable.

Making it Testable

In order to test the worker service the App is replaced with a mock object, which is fine for checking that the completion method is called, but fails in that it could not be monitored to see when the worker thread has finished.

A stop gap solution involved entering a while loop for 10 seconds until I was sure the thread was complete, before letting the test continue and validate that all the mocks we succesfully met.

Obviously this reeks, so I started looking at ways of exposing the worker thread so that I could monitor it directly, before realising that this was also leading me down the wrong path, as ideally the test shouldn't care about the internals of what is being tested.

Had I gone down that road, it would most likely have led to problems when trying to test what happens when it is fired multiple times for example.

Having decided that, I relented and did what I was initially trying to avoid, and made use of an event for the completion of the work, as is documented in a number of places as the correct way to test worker threads.

It turns out this made the most sense as the App is responsible for creating the Worker Class in the first place, so is the perfect place to register for the completion event, so that it can then be passed to the UI in the same way that it was before, so the GUI encapsulation is still well maintained (Assuming an interface is used).

This can then be tested by using an anonymous delegate to signal when the waiting should be end, and I no longer have to do any nasty waiting for an arbitrary length of time!