Post

Unit Testing NHibernate Mappings

What is the best approach to map your domain classes to the database using NHibernate? After doing some tests, I believe that the best answer is: it doesn’t matter. As long as you have unit tests in place for it, you are covered.

The same mapping issue that you will face using HBM files, you will encounter using FHN or Mapping By Code. Because of that, I did some experiments on what would be a good approach to unit test NHibernate mappings.

Project Setup

All the source code used in this post is also available here. The NuGet packages used were the following.

  • For the Domain library project: none.
  • For the Persistence library project: NHibernate and System.Data.SQLite.Core.
  • For the Tests library project: NHibernate, System.Data.SQLite.Core, NUnit and NUnit3TestAdapter.

The library NUnit3TestAdapter is all you need to run NUnit unit tests from any version of Visual Studio.

Using an In-Memory Helper Class

The following is the NHibernate helper class that we will be using to open new connections. It is designed to create an in-memory instance of SQLite database per unit test class or unit test method. Doing so, we can isolate our unit tests on its own contained database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using System;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Tool.hbm2ddl;
using System.Data.SQLite;

namespace Persistence
{
    public class InMemorySessionHelper : ISessionHelper, IDisposable
    {
        private const string Connectionstring = "Data Source=:memory:";

        private readonly Configuration _config;
        private readonly ISessionFactory _sessionFactory;
        private SQLiteConnection _connection;

        public InMemorySessionHelper()
        {
            _config = new Configuration()
                .DataBaseIntegration(db =>
                {
                    db.Dialect<SQLiteDialect>();
                    db.Driver<SQLite20Driver>();
                    db.ConnectionString = Connectionstring;
                    db.LogFormattedSql = true;
                    db.LogSqlInConsole = true;
                })
                .AddAssembly("Persistence");

            _sessionFactory = _config.BuildSessionFactory();
        }

        public void Dispose()
        {
            _connection?.Dispose();
        }

        public ISession OpenSession()
        {
            return _sessionFactory.OpenSession(GetConnection());
        }

        private SQLiteConnection GetConnection()
        {
            if (_connection == null)
            {
                _connection = new SQLiteConnection(Connectionstring);
                _connection.Open();

                SchemaExport se = new SchemaExport(_config);
                se.Execute(true, true, false, _connection, null);
            }

            return _connection;
        }
    }
}

Base Test Fixture Class

The BaseFixture class is designed to create a new instance of the database for each Test Fixture. It will help us create a new instance of InMemorySessionHelper for each unit test. For what I did so far, running them per test class was enough.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using NHibernate;
using NUnit.Framework;
using Persistence;

namespace Tests.Mappings
{
    public class BaseFixture
    {
        private InMemorySessionHelper _sessionHelper;

        [OneTimeSetUp]
        public void Setup()
        {
            _sessionHelper = new InMemorySessionHelper();
        }

        protected ISession OpenSession()
        {
            return _sessionHelper.OpenSession();
        }

        [OneTimeTearDown]
        public void TearDown()
        {
            _sessionHelper.Dispose();
        }
    }
}

The OpenSession method will be used to create new sessions inside each unit test. The in-memory database will stay alive until the connection is open. The TearDown method will be responsible for disposing of the session helper, which will close the connection.

A Sample Unit Test

The sample test demonstrates how we can extend the BaseFixture class to execute a unit test that validates the data mapping for a Template Fields property.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using Domain.Entities;
using NUnit.Framework;
using System.Linq;

namespace Tests.Mappings
{
  [TestFixture]
  public class TemplateMappingFixture : BaseInMemoryFixture
  {
    [Test]
    public void TemplateFieldsProperty()
    {
      object entityId = null;

      using (var session = OpenSession())
      {
        using (var transaction = session.BeginTransaction())
        {
          var template = new Template();
          template.AddField(new Field("FieldOne"));
          template.AddField(new Field("FieldTwo"));

          entityId = session.Save(template);

          transaction.Commit();
        }
      }

      using (var session = OpenSession())
      {
        var entity = session.Get<Template>(entityId);

        Assert.NotNull(entity);
        Assert.NotNull(entity.Fields);
        Assert.AreEqual(2, entity.Fields.Count);
        Assert.Contains("FieldOne", entity.Fields.Select(x => x.Name).ToList());
        Assert.Contains("FieldTwo", entity.Fields.Select(x => x.Name).ToList());
      }
    }
  }
}

The point here is that you don’t need to hit a real database or even a development database, to validate that your ORM mapping is working as expected.

This post is licensed under CC BY 4.0 by the author.