Testen von Datenbanken – 5 Testfälle in zur Laufzeit generierter Datenbank

28. April 2015

Bei diesem Vorgehen werden die Testfälle gegen eine eigene Testdatenbank ausgeführt, die erst zur Laufzeit erstellt und angesprochen wird. Als Voraussetzung benötigt man die geskriptete Datenbankstrukturen und vorbereitete Testdaten. Außerdem muss man berechtigt sein auf dem MS SQL Server eine Datenbank zu erstellen und wieder zu löschen.

Alles was man im SQL Management Studio administriert kann man auch als Skript ausführen. Also auch das Erstellen einer Datenbank. Man muss lediglich SQL Befehle wie "CREATE DATABASE" ausführen.
Als erstes benötigt man eine Verbindung zum MS SQL Server und die entsprechenden Berechtigungen. Nachdem die Strukturen erstellt und sie mit Testdaten gefüllt ist, kann sie für den Test über eine separate Datenbankverbindung verwendet werden. Es sollte auf keinen Fall die erste Verbindung für die Durchführung des Tests genutzt werden. Darunter läuft der Code mit anderen Berechtigungen als später in Produktion. Am Ende sollte sie dann gelöscht werden. So kann man für jeden Testfall einzeln oder für alle Testfälle gemeinsam vorgehen.

Testablauf am Beispiel der Referenzanwendung

  1. Eine Verbindung mit entsprechenden Berechtigungen wird zum MS SQL Server geöffnet. Beispielsweise zur [tempdb].
  2. Die Datenbank wird über Skripte erzeugt.
  3. Die Testdaten zum 1.1.2014 werden in die Tabelle [dbo].[Source] eingespielt.
  4. Eine weitere Verbindung wird geöffnet, diesmal zur neuen Datenbank. Diese Verbindung verwendet die anwendungsspezifischen Berechtigungen, die erste Verbindung wäre deswegen für die Tests ungeeignet.
  5. Die Tabelle [dbo].[Target_Sum] wird gelöscht.
    • [dbo].[Delete_Target_Sum_ByCalcDate]
  6. Die Tabelle [dbo].[Target_Sum] wird befüllt.
    • [dbo].[Insert_Target_ByCalcDate]
  7. Es wird geprüft ob die Daten wie erwartet in der Tabelle [dbo].[Target_Sum] vorliegen.
    • Die Daten werden vom Test in eine Datei exportiert.
    • Die Datei wird mit einer erwarteten Datei verglichen.
  8. Die erstellte Datenbank wird gelöscht.
[TestInitialize]

public void Init()

{

    m_testDatenmangerRoot = new DBManager("ROOT", false); // Transaction muss nicht geprüft werden, ist ja ne eigene DB!

    m_testDatenmangerDB = new DBManager("DB", false); //Root geht auf eine andere DB

 

    try

    {

        m_testDatenmangerRoot.ExecFile(@".ScripteDropDB.sql");

    }

    catch { }

            

    m_testDatenmangerRoot.ExecFile(@".ScripteCreateDB.sql");

    m_testDatenmangerRoot.ExecFile(@".ScripteGrants.sql");

    m_testDatenmangerRoot.ExecFile(@".ScripteCreateObjects.sql");

 

    m_testDatenmangerRoot.ExecFile(@".ScripteInsertData.sql");

}

 

[TestMethod]

public void DALTest_CreateDB()

{

    DBManagerTarget mgr = new DBManagerTarget();

    DateTime calcDate = new DateTime(2014, 01, 01);

    mgr.DeleteTargetSum(calcDate);

    mgr.FillTarget(calcDate);

 

    string filename = "DALTest_CreateDB_UnitTest1.txt";

    m_testDatenmangerDB.ReadToFile(

        DIRTARGET + filename,

        "SELECT * FROM dbo.Target_Sum WHERE CalcDate='2014-01-01';");

 

    CompareHelper.AssertAreEual(DIRSOURCE + filename, DIRTARGET + filename);

}

 

[TestCleanup]

public void CleanUp()

{

    m_testDatenmangerRoot.ExecFile(@".ScripteDropDB.sql");

    m_testDatenmangerRoot = null;

    m_testDatenmangerDB = null;

}

<connectionStrings>

  <clear/>

  <add name="ROOT" connectionString="...Initial Catalog=tempdb;...User Id=sa;..."/>

  <add name="DB" connectionString="...Initial Catalog=DBTestingCreate;..."/>

</connectionStrings>

CREATE DATABASE [DBTestingCreate] 

...

 

USE [DBTestingCreate]

CREATE USER [UsrDBTesting] FOR LOGIN [UsrDev] WITH DEFAULT_SCHEMA=[dbo]

GRANT CONNECT TO [UsrDBTesting] AS [dbo]

ALTER ROLE [db_datareader] ADD MEMBER [UsrDBTesting]

ALTER ROLE [db_datawriter] ADD MEMBER [UsrDBTesting]

ALTER DATABASE [DBTestingCreate] SET  READ_WRITE 

...

 

USE [DBTestingCreate]

CREATE TABLE [dbo].[Source] ...

CREATE TABLE [dbo].[Target_Code] ...

CREATE TABLE [dbo].[Target_Sum] ...

 

CREATE PROCEDURE [dbo].[Delete_Target_Sum_ByCalcDate] ...

CREATE PROCEDURE [dbo].[Insert_Target_ByCalcDate] ...

...

 

USE [DBTestingCreate]

INSERT INTO [dbo].[Source] ( [CalcDate],[Code],[Value]) VALUES ('2014-01-01','A','10');

...

 

DROP DATABASE [DBTestingCreate]

...

Ergebnis: Erfolgreich

SELECT * FROM dbo.Target_Sum WHERE CalcDate='2014-01-01';

 

CalcDate(datetime),ID(int),Value(float)

 

01.01.2014 00:00:00,1,10

01.01.2014 00:00:00,2,20

01.01.2014 00:00:00,3,30

01.01.2014 00:00:00,4,40

Da die Datenbank für jedes Testszenario neu erstellt wird kann die Tabelle [dbo].[Target_Code] nie mit falschen Werten gefüllt sein.

Vorteile

  • Daten sind exakt auf die Tests zugeschnitten
    • Entwicklungsdaten werden nicht geändert
    • Sogar IDENTITY Spalten können exakt getestet werden
  • Aufsetzten der DB wird gleich mitgetestet
  • Pro Testszenarien ist es möglich eine eigene Datenbank mit unterschiedlichen Testdaten zu erzeugen

Nachteile

  • Laufzeiten der Tests
  • Komplexität des Tests steigt (eine ganze Datenbank wird erstellt)
  • Ggf. hoher Aufwand bei Fehlersuche, da Datenbank am Ende gelöscht wird

Das im vorherigen Beitrag beschriebene Feature des MS SQL Server Express inspirierte mich zu diesem Vorgehen. Ich habe es bisher nur in diesem Beispiel ausgearbeitet.

Anstatt die Datenbank komplett neu zu erstellen, könnte man auch im MS SQL Server, über die richtigen Befehle, Datenbankdateien mounten. Da aber in jedem Projekt die Datenbankstrukturen als Skripte vorliegen sollten, habe ich diese etwas komplizierte Variante gewählt, sie wäre auch hervorragend geeignet um gleich noch die Deploymentskripte mit zu testen.

zur Übersicht