2011/04/18

Integration tests, NHibernate and Sqlite, part 3 – enhanced in memory database

Integration tests, NHibernate and Sqlite, part 1
Integration tests, NHibernate and Sqlite, part 2 – in file database
Integration tests, NHibernate and Sqlite, part 3 – enhanced in memory database

In previous post I introduced a database scope that builds the database schema only once per test suit run. Some performance was sacrificed to achieve this: the database had to be stored in a file. Database stored in memory is obviously faster than database stored in a file. Is there a way how to move the database back to memory?

Yes, we can build a master database in-memory and then make copy of this master database for each test. The problem is how to duplicate the master database. Sqlite provides Online Backup API, which is not available in System.Data.SQLite wrapper yet.

Sqlite Backup API
Fortunately it is quite easy to wrap the required API:
[DllImport("System.Data.SQLite.DLL", CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr sqlite3_backup_init(IntPtr destDb, byte[] destname, IntPtr srcDB, byte[] srcname);

[DllImport("System.Data.SQLite.DLL", CallingConvention = CallingConvention.Cdecl)]
internal static extern int sqlite3_backup_step(IntPtr backup, int pages);

[DllImport("System.Data.SQLite.DLL", CallingConvention = CallingConvention.Cdecl)]
internal static extern int sqlite3_backup_finish(IntPtr backup);

public static void Backup(SQLiteConnection source, SQLiteConnection destination)
{
    IntPtr sourceHandle = GetConnectionHandle(source);
    IntPtr destinationHandle = GetConnectionHandle(destination);

    IntPtr backupHandle = sqlite3_backup_init(destinationHandle, SQLiteConvert.ToUTF8("main"), sourceHandle, SQLiteConvert.ToUTF8("main"));
    sqlite3_backup_step(backupHandle, -1);
    sqlite3_backup_finish(backupHandle);
}

There is only one catch. Handles needed by Sqlite API are stored as private fields in System.Data.SQLite wrapper. But it can be retrieved by this little trick:
private static IntPtr GetConnectionHandle(SQLiteConnection source)
{
    object sqlLite3 = GetPrivateFieldValue(source, "_sql");
    object connectionHandle = GetPrivateFieldValue(sqlLite3, "_sql");
    IntPtr handle = (IntPtr)GetPrivateFieldValue(connectionHandle, "handle");

    return handle;
}

private static object GetPrivateFieldValue(object instance, string fieldName)
{
    var filedType = instance.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);

    object result = filedType.GetValue(instance);
    return result;
}

Yes, it is a hacky solution but it works well and it can be viewed as temporary replacement for the missing wrapper part.

SqliteInMemoryPrivateScope
Modification of SqliteInFilePrivateScope is easy with such helper method.

The master database is duplicated by the new wrapper method:
public SqliteInMemoryPrivateScope()
{
    EnsureMasterDatabaseExistence();

    this.privateConnection = new SQLiteConnection(connectionString);
    this.privateConnection.Open();

    SqliteBackup.Backup(masterConnection, this.privateConnection);
}

And a connection to the master database must be held by the scope instance. It is similar to SqliteInMemorySharedScope:
private static SQLiteConnection masterConnection;

private void EnsureMasterDatabaseExistence()
{
        // to support asynchronous scenario
        lock (configurationSync) {
            if (configuration == null) {
                configuration = BuildConfiguration ();
                sessionFactory = configuration.BuildSessionFactory();

                masterConnection = new SQLiteConnection(connectionString);
                masterConnection.Open();

                SchemaExport schemaExport = new SchemaExport(configuration);
                schemaExport.Execute(false, true, false, masterConnection, TextWriter.Null);
            }
        }
}

Now we have a database scope that builds configuration, session factory and database schema only once per test suit run which saves a lot of time. In addition the database is stored in memory so integration tests run blazingly fast. The isolation between tests is very good and the test can be executed in parallel. What to want more?:).

Full implementation of SqliteInMemoryPrivateScope can be found here.
Complete source code can be downloaded here.

2011/04/11

Integration tests, NHibernate and Sqlite, part 2 – in file database

Integration tests, NHibernate and Sqlite, part 1
Integration tests, NHibernate and Sqlite, part 2 – in file database
Integration tests, NHibernate and Sqlite, part 3 – enhanced in memory database

SqliteInMemorySharedScope issues
The disadvantage is that the time needed byc SqliteInMemorySharedSope instantiation grows with the size of the tested domain model. When the domain model is complex enough then execution of test suit may take unacceptable amount of time.

Storing the database in a file
Sqlite is able to store the database in a file as well. We create a database with its schema at first and store it in a file, let’s call it master file. Then it is easy to create a copy of the master file. Individual tests touch only own copy of the master file. So isolation is preserved and dependency between the time of database scope instantiation and complexity of the domain is significantly more moderate.

SqliteInFileDatabaseScope
The new session is connected to the private copy of the master file.
public ISession OpenSession()
{
    ISession session = SqliteInFilePrivateScope.sessionFactory.OpenSession(this.privateConnection);

    return session;
}

The private connection is created in the default constructor.
public SqliteInFilePrivateScope()
{
    EnsureMasterDatabaseExistence();

    // to avoid name collisions in parallel scenario
    this.privateDatabaseFileName = Guid.NewGuid().ToString() + ".db";

    File.Copy(masterDatabaseFileName, this.privateDatabaseFileName);

    string connectionString = BuildConnectionString(this.privateDatabaseFileName);
    this.privateConnection = new SQLiteConnection(connectionString);
    this.privateConnection.Open();
}

The master file, configuration and session factory are created only once per test suit run.
private static object configurationSync = new object();

private void EnsureMasterDatabaseExistence()
{
    // to support parallel execution of unit tests
    lock (configurationSync) {
        if (configuration == null) {
            string connectionString = BuildConnectionString(masterDatabaseFileName);
            configuration = BuildConfiguration(connectionString);
            sessionFactory = configuration.BuildSessionFactory();

            SchemaExport schemaExport = new SchemaExport(configuration);
            schemaExport.Execute(false, true, false); 
        }
    }
}

Connection string factoring:
private static string BuildConnectionString(string databaseName)
{
    return string.Format("Data Source={0};Version=3;New=True;", databaseName);
}

And BuildConfiguration is delegate that is injected on test suit initialization (for more details see this post):
public static Func<string, NHibernate.Cfg.Configuration> BuildConfiguration;

The full example is here.
The same example but using Microsoft Sql Server Compact Edition instead of Sqlite is here.
Complete source code can be downloaded here.

Why to support parallel scenarios in Visual Studio 2010
Tests are executed sequentially by default in Visual Studio and there is no setting to turn on parallel execution in VS2010 GUI. To achieve this, the file with test settings must be tweaked. The name of the file is Local.testsetings by default:
<TestSettings name="Local" id="caf44e0f-3ee4-46bf-a6ed-73aea6d93533" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
  <Description>These are default test settings for a local test run.</Description>
  <Deployment enabled="false" />
  <Execution parallelTestCount="2">
    <TestTypeSpecific />
    <AgentRule name="Execution Agents">
    </AgentRule>
  </Execution>
</TestSettings>

Attribute parallelTestCount specifies how many tests are executed at one time. Parallel execution makes sense in these days of multicore processors because it can reduce the execution time of whole test suit. And it counts when doing continuous integration. You can find much more information in this blog post.

2011/04/04

Unit tests initialization in Visual Studio 2010

NHDatabaseScopes problem
I am going to dig into a nasty detail of SqliteInMemorySharedScope class in this post. The FNH configuration is built by the constructor of this class. NH configuration needs to know which assembly to load the mapping metadata from. If the knowledge is included right in the constructor then the SqliteInMemorySharedScope (like in previous post) is coupled with tested domain model and cannot be reused in other project. It would be neat to inject the metadata or just the configuration into it. The configuration and session factory is shared between all unit tests and there is usually no particular execution order defined for tests. The question is: how to inject all information needed by SqliteInMemorySharedScope?

Unit test initialization
VS2010 offers a way how run a code before any test is executed. Just decorate a static method in test class with AssemblyInitialize attribute:
[TestClass]
public class TestEnvironmentInitialization
{
   [AssemblyInitialize]
   public static void Initialize(TestContext context)
   {
      // any initialization code

Some code can be executed after all tests were finished as well – attribute AssemblyCleanup.
The execution order is:
1)    Static method marked by AssemblyInitialize attribute.
2)    Default constructor of a test class.
3)    Method marked by TestInitialize attribute.
4)    Method marked by TestMethod attribute.
5)    Method marked by TestCleanup attribute.
6)    Static method marked by AssemblyCleanup attribute.

SqliteInMemorySharedScope decoupling from domain model
Back to the SqliteInMemorySharedScope class. A delegate that builds NH configuration is injected instead of configuration itself. The advantage, but questionable, is lazy initialization. Configuration is built only when it is really needed.
SqliteInMemorySharedScope initialization:
[TestClass]
public class TestEnvironmentInitialization
{
    private static NHibernate.Cfg.Configuration BuildSqliteConfiguration(string connectionString)
    {
    var config = Fluently.Configure()
        .Database(SQLiteConfiguration.Standard.ConnectionString(connectionString))
        .Mappings(m => {
            m.FluentMappings.AddFromAssembly(typeof(Order).Assembly);
        })
        .BuildConfiguration();

    return config;
}

[AssemblyInitialize]
public static void Initialize(TestContext context)
{
    BuildConfiguration = BuildSqliteConfiguration;

And simple modification of SqliteInMemorySharedScope:
public class SqliteInMemorySharedScope
{
    public static Func<string, NHibernate.Cfg.Configuration> BuildConfiguration;

   public SqliteInMemorySharedScope()
   {
      if (configuration == null) {
         configuration = BuildConfiguration(connectionString);
      }
SqliteInMemorySharedScope is decoupled from the domain model now and can be reused in multiple projects.

Source code can be downloaded here.