SQLScript Unit Tests, das End-User Test Framework

SQLScript Unit Tests, das End-User Test Framework

Veröffentlicht am 30. Dezember 2019 von

Jörg Brandeis

| News | SQLScript |

Mit der Version 2.0 SPS04 der SAP HANA Datenbank wurde das sogenannte "End-User Test Framework in SQLScript" eingeführt. Damit ist es möglich, SQLScript Unit Tests in Form von Benutzerdefinierte Bibliotheken (UDL) zu erstellen. Diese können dann mit dem Aufruf der Prozedur SQLSCRIPT_RUN_TESTS_ON_ORIGINAL_DATA automatisch ausgeführt werden.

Was wird getestet?

Meistens werden Testfälle verwendet, um für ein Unterprogramm zu prüfen, ob bei einer vorgegebenen Eingabe auch die erwartete Ausgabe erzeugt wird. Im Falle von Datenbanken handelt es sich also um Prozeduren und Funktionen, die vornehmlich getestet werden. Es lässt sich aber auch der Zustand der Datenbank testen, in dem wir beispielsweise die Anzahl der Datensätze in einer Tabelle abfragen. So können wir bei dem folgenden Beispiel prüfen, ob die Skripte für das Demo Datenmodell erfolgreich waren, und die erwartete Anzahl von Datensätzen generiert haben.

Beispiel

Um das Konzept des Test Framework zu erläutern, starten wir mit einem einfachen Beispiel. Im Listing 1.2 finden Sie eine Testbibliothek, die wirklich nur das Nötigste enthält. Sie besteht nur aus einer Testprozedur T_AUFGABEN_CNT. Diese prüft, ob in der Tabelle AUFGABEN genau 1000 Einträge enthalten sind.

CREATE LIBRARY test_demo_dm 
LANGUAGE SQLSCRIPT TEST 
AS BEGIN 

  @test()
  PUBLIC PROCEDURE t_aufgaben_cnt 
  AS BEGIN
    USING sqlscript_test AS test;
    test:expect_eq((SELECT COUNT(*) FROM aufgaben),
                     1000 );
  END;    
END;

Um diesen SQLScript Unit Test in unserer Testbibliothek auszuführen, rufen wir in der SQL-Konsole die folgende Prozedur auf. Gegebenenfalls müssen Sie den Namen des Schemas anpassen:

CALL SQLSCRIPT_RUN_TESTS_ON_ORIGINAL_DATA('{"schema":"SYSTEM",
                          "library":"TEST_DEMO_DM"}', ?, ?, ?);

Das Ergebnis dieses Prozeduraufrufes sind 3 Tabellen. In unserem Falle ist nur in der Ersten auch tatsächlich ein Eintrag vorhanden, weil der Test erfolgreich war. Sobald ein Test scheitert, wird in der 2. Tabelle eine Meldung ausgegeben und die 3. Tabelle enthält Details zum Call-Stack der Fehler. Weiter unten ist ein Beispiel für die Ausgegebenen Tabellen

Die Sprache der Tests

Als erstes fällt an dem kleinen Beispiel auf, dass die Bibliothek mit der Sprache SQLSCRIPT TEST angelegt wird. Damit werden Testbibliotheken von produktiven Bibliotheken unterschieden. Tatsächlich handelt es sich bei der Implementierung der Test aber auch um SQLScript.

Pragmas für Prozeduren

Um die einzelnen Prozeduren der Testbibliotheken zu klassifizieren, werden Pragmas eingesetzt. Die wichtigste Rolle spielen die die parameterlosen Testprozeduren. Diese werden durch ein vorangestelltes Pragma @test() markiert.

Um einen definierten Systemzustand vor dem Test herzustellen, kann mit dem Pragma @TestSetUpLibrary() eine entsprechende Prozedur markiert werden.

Nach der Ausführung der Tests wird gegebenenfalls die Prozedur mit dem Pragma @TestTearDownLibrary() aufgerufen, um nach den Testprozeduren wieder aufzuräumen.

Prüfprozeduren

Innerhalb der Testprozeduren können mit Hilfe der Prüfprozeduren der Bibliothek SQLSCRIPT_TEST die Zustände des Programms verifiziert werden. Die unterschiedlichen Prüfprozeduren finden Sie in Tabelle 1.1. Als Parameter Wert 1 bzw. 2 kann jeder beliebiger skalarer Ausdruck verwendet werden.

PrüfprozedurBeschreibung
EXPECT_EQ(<Wert1>, <Wert2>)Prüft, ob beide Werte gleich sind
EXPECT_NE(<Wert1>, <Wert2>)Prüft, ob die beiden Werte unterschiedlich sind
EXPECT_GE(<Wert1>, <Wert2>)Prüft, ob Wert 1 grösser oder gleich dem Wert 2 ist
EXPECT_GT(<Wert1>, <Wert2>)Prüft, ob Wert 1 grösser als Wert 2 ist
EXPECT_LE(<Wert1>, <Wert2>)Prüft, ob Wert 1 kleiner oder gleich dem Wert 2 ist
EXPECT_LT(<Wert1>, <Wert2>)Prüft, ob Wert 1 kleiner als Wert 2 ist
EXPECT_NULL(<Wert>)Prüft, ob der Wert NULL ist
EXPECT_TABLE_EQ(<Tabelle1>, <Tabelle2>[,<Reihenfolge ignorieren>])Prüft, ob die beiden Tabellen identisch sind. Die Reihenfolge spielt keine Rolle, solange nicht das entsprechende Flag auf FALSE gesetzt wird.
FAIL(<Nachricht>)Der Test wird auf gescheitert gesetzt und die Nachricht wird ausgegeben

Modularisierung in der Testbibliothek

Diese Prüfprozeduren unserer SQLScript Unit Tests müssen nicht direkt in der Testprozedur aufgerufen werden. Es kann innerhalb der Testbibliothek auch weiter modularisiert werden. Wenn beispielsweise mehrere Tabellen auf die Anzahl der enthaltenen Datensätze überprüft werden sollten, so bietet es sich an, diesen Teil des Codes in eine separate Prozedur auszulagern, um unnötige Wiederholungen zu vermeiden.

In Listing 1.3 wurde die Logik, um die Anzahl der Datensätze innerhalb einer Tabelle zu prüfen, in eine private Prozedur ausgelagert. Damit ist es ein Leichtes, auch sehr viele Tabellen zu prüfen, ohne dass man den gleichen Code jedes Mal wiederholen muss.

CREATE LIBRARY TEST_DEMO_DM 
LANGUAGE SQLSCRIPT TEST 
AS BEGIN 
  
  PRIVATE PROCEDURE check_table_count
    (IN iv_tabname NVARCHAR(30),
     IN iv_expected_count INTEGER)
  AS BEGIN
    USING sqlscript_test AS test;
    DECLARE lv_query NVARCHAR(5000);
    DECLARE lv_count INTEGER;
    
    lv_query = 'SELECT COUNT(*) FROM ' || :iv_tabname;
    EXEC lv_query INTO lv_count;
    test:expect_eq( :lv_count, :iv_expected_count );
      
  END;
  
  @test()
  PUBLIC PROCEDURE t_table_count 
  AS BEGIN
      check_table_count( 'AUFGABEN', 1000);
      check_table_count( 'BENUTZER', 30);
      check_table_count( 'STATUS', 6); 
      check_table_count( 'STATUS_TEXT', 12);
    --check_table_count( 'STATUS_TEXT', 13);
  END;     
END;

Testergebnis

Wenn wir die obige Testbibliothek ausführen und alles richtig gemacht haben, dann werden wir keine Fehlermeldung erhalten. Zur Demonstration habe ich die Parameter für die Tabelle STATUS_TEXT verändert. Das Ergebnis der drei Rückgabetabellen des Tests sehen Sie in den folgenden drei Abbildungen. Die erste Tabelle zeigt uns, dass die Testprozedur in der Testbibliothek mit dem Ergebnis FAILED ausgeführt wurde:

Ergebnis der Testausführung

In der zweiten Tabellen finden wir den Grund für den Fehler. In unserem Falle stimmt die Anzahl der Datensätze nicht.

Fehlermeldungen der Testausführung

Die dritte Tabelle liefert jetzt den zugehörigen Call Stack.

Call Stack der gemeldeten Fehler

Weiterführende Konzepte

Neben diesen grundlegenden Konzepten gibt es auch noch die Möglichkeit, mit Hilfe von sogenannten Konfigurationen unterschiedliche Vorbedingungen für die Testprozeduren zu erstellen.

Die Testprozeduren wiederum können durch eine Klassifizierung in Gruppen eingeteilt werden.

Beim Aufruf der Prozedur SQLSCRIPT_RUN_TESTS_ON_ORIGINAL_DATA kann man ein JSON Dokument mitgeben, dass im Detail festlegt, welche Prozeduren mit welchen Klassifikationen in welche Konfigurationen ausgeführt werden sollen.  

Diese Details finden Sie in der Referenzdokumentation SAP HANA SQLScript Reference for SAP HANA Platform.

Bewertung

Mit dem Test Framework haben wir jetzt ein sehr nützliches Instrument in der Hand, mit dem wir unsere Programme auf der Ebene der Datenbank automatisiert testen können. Die Erstellung von Unit Tests ist mittlerweile weit verbreiteter Standard, der somit auch für SQLScript Entwicklungen anwendbar ist. Auch die Testgetriebene Entwicklung (TDD) ist mit dem Framework möglich.