Copy SQLite database to memory for faster unit tests

In C # / nHibernate projects, I use SQLite to unit test my code using the method described here: http://ayende.com/blog/3983/nhibernate-unit-testing .

However, I found that creating and setting up a database in memory usually takes about 150 ms. I have a lot of unit test, so it adds up quickly.

I want to create and configure a database once, save it in a static variable and copy it every time a unit test needs a database.

How to back up a database in memory?

At first I tried to create a named database in memory. According to https://www.sqlite.org/inmemorydb.html this is possible. I used to have:

private const string ConnectionString = "Data Source=:memory:;Version=3;"; 

Connecting lines I tried:

  private const string ConnectionString = "FullUri=file:memorydb.db?mode=memory&cache=shared"; private const string ConnectionString2 = "FullUri=file:memorydb2.db?mode=memory&cache=shared"; 

So now I just need to figure out how to quickly copy content from one to another? I am almost there: I can create two databases in memory and call "BackupDatabase" to copy the database.

However, unit test behaves like an instance database, has no tables, not even a prototype database.

  private static ISessionFactory _prototypeSessionFactory; private const string InstanceConnectionString = "FullUri=file:memorydb.db?mode=memory&cache=shared"; private const string PrototypeConnectionString = "FullUri=file:memorydb2.db?mode=memory&cache=shared"; private SQLiteConnection _instanceConnection; private ISessionFactory _instanceSessionFactory; public DatabaseScope(Assembly assembly) { var prototyeConfiguration = SQLiteConfiguration.Standard.ConnectionString(PrototypeConnectionString); var cfg = Fluently .Configure() .Database(prototyeConfiguration) .Mappings(m => m.HbmMappings.AddFromAssembly(assembly)); cfg.ExposeConfiguration(BuildSchema); _prototypeSessionFactory = cfg.BuildSessionFactory(); var instanceConfiguration = SQLiteConfiguration.Standard.ConnectionString(InstanceConnectionString); _instanceSessionFactory = Fluently .Configure() .Database(instanceConfiguration) .BuildSessionFactory(); CopyDatabase(); } private void CopyDatabase() { var cnnIn = new SQLiteConnection(PrototypeConnectionString); var cnnOut = new SQLiteConnection(InstanceConnectionString); cnnIn.Open(); cnnOut.Open(); cnnIn.BackupDatabase(cnnOut, "main", "main", -1, null, -1); cnnIn.Close(); cnnOut.Close(); } 
+6
source share
2 answers

What I observed with SQLite in the memory bases is that as soon as you close the connection, everything in db disappears. To do what you want

  • Create a factory session for the backup database, open a session and build a schema, do not close this session until you finish the entire test package

  • Create a factory session for your target database, open a session, and use the connection from this session object and the connection from the session created from step 1 to copy the data

  • Use the session you created in step 2 for testing, and close it after the test completes.

Another solution is to use one session to run several tests (all tests in one test device), then you do not need to create a new factory session for each test, but on testfixture

+1
source

I ended up with this working code. My unit test lasted from ten minutes to two minutes. (The code is a little simplified for readability)

 using System; using System.Data; using System.Data.SQLite; using System.IO; using System.Reflection; using FluentNHibernate.Cfg; using FluentNHibernate.Cfg.Db; using NHibernate; using NHibernate.Mapping; using NHibernate.Tool.hbm2ddl; namespace TestHelper.DbHelper.SqLite { public class DatabaseScope : IDisposable { private static Assembly _prototypeAssembly; private const string PrototypeConnectionString = "FullUri=file:prototype.db?mode=memory&cache=shared"; private static ISessionFactory _prototypeSessionFactory; private static SQLiteConnection _prototypeConnection; private const string InstanceConnectionString = "FullUri=file:instance.db?mode=memory&cache=shared"; private ISessionFactory _instanceSessionFactory; private SQLiteConnection _instanceConnection; public DatabaseScope(Assembly assembly) { InitDatabasePrototype(assembly); InitDatabaseInstance(); } private void InitDatabasePrototype(Assembly assembly) { if (_prototypeAssembly == assembly) return; if (_prototypeConnection != null) { _prototypeConnection.Close(); _prototypeConnection.Dispose(); _prototypeSessionFactory.Dispose(); } _prototypeAssembly = assembly; _prototypeConnection = new SQLiteConnection(PrototypeConnectionString); _prototypeConnection.Open(); _prototypeSessionFactory = Fluently .Configure() .Database(SQLiteConfiguration.Standard.ConnectionString(PrototypeConnectionString)) .Mappings(m => m.HbmMappings.AddFromAssembly(assembly)) .ExposeConfiguration(cfg => new SchemaExport(cfg).Execute(false, true, false, _prototypeConnection, null)) .BuildSessionFactory(); } private void InitDatabaseInstance() { _instanceSessionFactory = Fluently .Configure() .Database(SQLiteConfiguration.Standard.ConnectionString(InstanceConnectionString)) .Mappings(m => m.HbmMappings.AddFromAssembly(_prototypeAssembly)) .BuildSessionFactory(); _instanceConnection = new SQLiteConnection(InstanceConnectionString); _instanceConnection.Open(); _prototypeConnection.BackupDatabase(_instanceConnection, "main", "main", -1, null, -1); } public ISession OpenSession() { return _instanceSessionFactory.OpenSession(_instanceConnection); } public void Dispose() { _instanceConnection.Close(); _instanceConnection.Dispose(); _instanceSessionFactory.Dispose(); } } } 
+2
source

Source: https://habr.com/ru/post/989808/


All Articles