2015-06-26 1 views
6

In C#/nHibernate-Projekten verwende ich SQLite, um meinen Code zu testen, ungefähr mit der hier beschriebenen Methode: http://ayende.com/blog/3983/nhibernate-unit-testing.In-Memory-SQLite-Datenbank kopieren, um Unit-Tests schneller zu machen

Ich finde jedoch, dass das Erstellen und Konfigurieren der In-Memory-Datenbank in der Regel etwa 150 ms dauert. Ich habe viele Komponententests, also addiert sich das schnell.

Ich möchte die Datenbank einmal erstellen und konfigurieren, sie in einer statischen Variablen speichern und jedes Mal kopieren, wenn ein Komponententest eine Datenbank benötigt.

Wie kann ich eine In-Memory-Datenbank sichern?

Ich habe zuerst versucht, eine benannte In-Memory-Datenbank zu erstellen. Dies ist gemäß https://www.sqlite.org/inmemorydb.html möglich. Früher habe ich haben:

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

Verbindungszeichenfolgen Ich habe versucht, sind:

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, jetzt muss ich nur noch herausfinden, wie schnell Inhalte von einem zum anderen kopieren zu? Ich bin fast da: Ich kann zwei In-Memory-Datenbanken erstellen und "BackupDatabase" aufrufen, um die Datenbank zu kopieren.

Der Komponententest verhält sich jedoch wie die "Instanz" -Datenbank hat keine Tabellen, sogar die "Prototyp" -Datenbank.

+0

nach [diesem Artikel] (https://www.sqlite.org/backup.html) (siehe loadOrSaveDb Beispiel). Es scheint, dass Sie 'sqlite3_backup_init()' mit einem Zeiger auf die Zieldatenbank verwenden müssen. Aber ich denke nicht, dass es dein Problem lösen wird. Ihre Tests klingen wie ein Integrationstest nicht UT. Ich denke, Sie sollten in Betracht ziehen, ein spöttisches Framework zu verwenden und Ihre DB zu ersetzen. –

+0

Danke für deine Antwort, Old Fox. Du hast Recht: Sie sind teilweise Integrationstest. Und sie funktionieren perfekt, nur ein bisschen langsam, was für mehr als 300 Tests schmerzt. Ich freue mich nicht darauf, so viele Tests neu zu schreiben, aber um des Arguments willen: Wie würdest du die Datenbank ersetzen, indem du ein spöttisches Framework verwendest? (Ich überlege, was ich spöttisch mache: Ich benutze SQLite, um meine Datenbank zu überspielen, und stelle sicher, dass alles funktioniert, solange meine nHibernate-Mappings korrekt sind.) – realbart

+0

Ich denke, 300+ Integrationstests bestätigen das Mapping ein wenig zu viel ... Ich glaube, dein Code arbeitet direkt gegen 'NHibernate'. Wenn ja, würde ich Ihnen raten, die Tests in 2 Arten zu teilen: 1. Verhalten + rufen Sie den OEM korrekt (gefälschte OEM. Wahrscheinlich die meisten Ihrer vorhandenen Tests). 2. Integrationstests - OEM hat den Typ korrekt zugeordnet (wahrscheinlich 10% aus den mehr als 300 Tests). Über die Fälschung der DB, vergessen Sie es, es war meine Schuld .... –

Antwort

2

Ich landete mit dieser Arbeitscode. Die Testdauer meines Tests ging von über zehn Minuten auf unter zwei Minuten. (-Code leicht zur besseren Lesbarkeit vereinfacht)

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(); 
     } 
    } 
} 
1

Was ich mit SQLite in Speicherdatenbanken beobachtet habe, ist, dass, sobald Sie die Verbindung schließen, alles in der Datenbank verschwunden ist. So tun, was Sie wollen,

  1. Erstellen Sitzung Fabrik für die Backup-Datenbank, offene Session und bauen Schema nicht über diese Sitzung schließen, bis Sie Ihre gesamte Testsuite beenden

  2. Sitzung Fabrik erstellen für Ihre Zieldatenbank, offene Sitzung und verwendet, um die Verbindung von diesem Sitzungsobjekt und die Verbindung von Sitzung aus Schritt 1 erstellten Daten

  3. verwenden sie die Sitzung auf Schritt 2 für Test erstellt zu kopieren und schließen, sobald Test beendet ist

Ein andere Lösung ist die einzige Sitzung zu verwenden, um mehrere Tests (alle Tests in einzelner Testvorrichtung) ausführen, dann müssen Sie keine neue Sitzung Fabrik pro Test schaffen, aber pro TestFixture

+0

Erstellt das Schema nicht den zeitaufwendigsten Teil beim Erstellen einer SQLite-Datenbank aus Nhibernate-Mappings? Das ist der Schritt, den ich versuche, circomvent ... Es gibt keine "trunk alle Tabellen" -Befehl, oder? – realbart

+0

Ich bin nicht sicher, Schema-Erstellung ist der Engpass oder, Erstellen und Herstellen von Konnektivität. Eine Möglichkeit besteht darin, einmal im Speicher db anzulegen und dieselbe Sitzung für mehrere Tests mit Transaktionserstellung bei jedem Teststart wiederzuverwenden und am Ende des Tests zurückzurollen. –

+0

Jip. Ich habe es funktioniert nach Ihren Anweisungen. Ich habe die falsche db irgendwo anders im Code referenziert. – realbart

Verwandte Themen