Programmierung

Erstellung von Modultests mit C16Unit

Mit diesem Artikel möchte ich an das Thema „Automatisierbare Modultests mit CONZEPT 16“ anschließen und zeigen, wie Modultests (Unit-Tests) in der Praxis umsetzbar sind. In einem kurzen Tutorial gehe ich auf die Erstellung von automatisierbaren Tests ein und zeige die Verwendung des Tools C16Unit.

Am Ende des Artikels finden Sie den Download von C16Unit. Es handelt sich dabei um eine CONZEPT 16-Datenbank, die neben der Test-Applikation auch bereits mehrere exemplarische Modultests enthält. Sie können diese Datenbank verwenden, um bereits erste eigene Testfunktionen zu schreiben. Später können Sie die Ressourcen (5 Prozeduren und 1 Dialog) in Ihre Anwendung übernehmen, um dann Tests für die dort bereits vorhandenen Systemfunktionen zu erstellen.
Als Einführung wollen wir eine kleine Testfunktion schreiben, welche die nachfolgende Funktion StringReplace() testen soll (Prozedur SysFnc):

// ******************************************************
// Suchen und Ersetzen einer Zeichenkette
sub StringReplace
(
  aString           : alpha;  // Zeichenkette
  aStrSearch        : alpha;  // Zu ersetzende Zeichenkette
  aStrReplace       : alpha;  // Einzufügende Zeichenkette
  opt aOptions      : int;    // Optionen für StrFind()
)
: alpha;
  local
  {
    tPos            : int;
    tLenSearch      : int;
    tLenReplace     : int;
  }
{
  tLenSearch  # StrLen(aStrSearch);
  tLenReplace # StrLen(aStrReplace);

  // Zeichenkette finden
  for tPos # StrFind(aString, aStrSearch, 1, aOptions);
  loop tPos # StrFind(aString, aStrSearch, tPos, aOptions);
  while (tPos > 0)
  {
    // Zeichenkette ersetzen
    aString # StrDel(aString, tPos, tLenSearch);
    aString # StrIns(aString, aStrReplace, tPos);

    inc(tPos, tLenReplace + 1);
  }

  return(aString);
}

Ich empfehle, die Testfunktion nicht in derselben Prozedur zu erstellen, um eine Vermischung von Systemfunktionen und Tests zu verhindern. Mit der Zeit werden die Tests immer umfangreicher und durch eine entsprechende Trennung behält man stets den Überblick. Daher erstellen wir im CONZEPT 16-Prozedureditor eine neue Prozedur mit dem folgenden Inhalt und speichern sie unter dem Namen TstString:

@A+
@C+
@I:TLInc

// *******************************************************
// Initialisierung der Tests
sub init () {}

// *******************************************************
// Terminierung der Tests
sub term () {}

// *******************************************************
// Test der Funktion StringReplace
sub testStringReplace ()
{
}

In Zeile 3 wird die Prozedur „TLInc“ inkludiert, die eine Reihe nützlicher Funktionen bereitstellt, die wir im weiteren Verlauf verwenden werden. Die Funktionen „init“ und „term“ werden zu Beginn der Testausführung bzw. am Ende gestartet. Sie können genutzt werden, um Datenbereiche, Strukturen oder Datensätze bereitzustellen, die von allen Testfunktionen der Prozedur verwendet werden können.
Im ersten Schritt wollen wir nun einen sehr simplen Fall prüfen: In der Zeichenkette „abc“ soll das „b“ durch ein „x“ ersetzt werden. Dies entspricht dem folgenden Funktionsaufruf, den wir in den Rumpf der Funktion „testStringReplace“ einfügen.

tValue # SysStr:StringReplace('abc', 'b', 'x');

Wir erwarten nach diesem Aufruf, dass die alphanumerische Variable tValue den Wert „axc“ enthält. Dies lässt sich mithilfe der Funktion „TstEqualA()“ überprüfen. TstEqualA() erwartet zwei Zeichenketten als Parameter, die identisch (engl. equal) sein sollten. Im positiven Fall (beide Werte identisch) wird die Funktion ohne Meldung zurückkehren, im anderen Fall wird sie eine Fehlermeldung ausgeben. Die Fehlermeldung kann durch einen eigenen Fehlertext (Parameter 3) ergänzt werden:

TstEqualA(tValue, 'axc', 'Ersetzung abc nach axc');

Wir fügen diese Zeile ans Ende der Funktion „testStringReplace“ ein und starten die aktuelle Prozedur „TstString“ durch die Tastenkombination STRG + T. Es wird automatisch C16Unit ausgeführt und unsere Testfunktion abgearbeitet.

Der grüne Fortschrittsbalken zeigt an, dass alle Tests erfolgreich durchlaufen sind. Über den Registerreiter „Testprotokoll“ werden die durchgeführten Testfunktionen aufgelistet und durch die Farbe Grün wird deren Erfolg symbolisiert.
C16Unit erkennt Testfunktionen anhand des Präfixes „test“. Testprozeduren werden standardmäßig über das Präfix „Tst“ definiert. In meinem nächsten Blog-Eintrag werde ich u.a. zeigen, wie diese Werte angepasst werden können.
Zunächst wollen wir unsere Funktion „testStringReplace“ erweitern, um die folgenden Fälle zu überprüfen:

// Entfernen von Zeichen (Ersetzen durch Leerstring)
tValue # SysFnc:StringReplace('abc', 'b', '');
TstEqualA(tValue, 'ac', 'Zeichen b entfernen');

// Verdoppelung eines Zeichens
tValue # SysFnc:StringReplace('abc', 'b', 'bb');
TstEqualA(tValue, 'abbc', 'Verdoppelung von b');

// Mehrfache Ersetzung
tValue # SysFnc:StringReplace('abbc', 'b', 'x');
TstEqualA(tValue, 'axxc', 'Mehrfache Ersetzung');

Die erneute Ausführung zeigt, dass nun ein Fehler festgestellt wurde.

Der Testfall „Mehrfache Ersetzung“ scheint fehlzuschlagen. Hierbei sollten zwei aufeinander folgende Zeichen „b“ jeweils durch das Zeichen „x“ ersetzt werden. Doch warum war dies nicht erfolgreich?
Nach genauerer Analyse lässt sich die Ursache innerhalb der Funktion StringReplace() finden: Nach dem ersten Schleifendurchlauf ist die ursprüngliche Zeichenkette „abbc“ in „axbc“ verändert worden. Der Positionszeiger, ab dem nun weiter nach dem Wert „b“ gesucht wird, befindet sich auf Position 4. Beim nächsten Suchlauf wird das zweite „b“ nicht gefunden, da dessen Position 3 übersprungen wurde.
Damit konnte dieser Test bereits auf einen Fehler in der Systemfunktion hinweisen. Eine Korrektur müsste in der Zeile

inc(tPos, tLenReplace + 1);

vorgenommen werden, sodass dies in

inc(tPos, tLenReplace);

geändert wird. In jedem Fall sollte die Testfunktion nach einer Änderung an der Systemfunktion erneut ausgeführt werden. Auf diese Weise kann geprüft werden, ob der Fehler wirklich behoben wurde und sich keine neuen Fehler eingeschlichen haben.
Nach dieser Einleitung sind Sie jetzt in der Lage, eigene Testfunktionen zu schreiben und diese mit C16Unit auszuführen. Neben der verwendeten Funktion TstEqualA() können die folgenden ebenfalls hilfreich sein:

  • TstEqualI(): Übereinstimmung zweier Integer-Zahlen prüfen
  • TstEqualL(): Übereinstimmung zweier logischer Werte prüfen
  • TstOk(): Funktionsresultat = _ErrOk prüfen
  • TstTrue(): Funktionsresultat = true prüfen
  • TstLogWrite(): Protokolleintrag schreiben

Werfen Sie einen Blick in die Prozedur „TstExample“, um beispielhafte Aufrufe der Befehle zu sehen.
Im nächsten Artikel werde ich darauf eingehen, wie sich die Testerstellung in Ihren Entwicklungsprozess integrieren lässt. Neben allgemeinen Strategien werden weitere Konzepte von C16Unit erläutert und die Vorteile und Einschränkungen dieses Vorgehens diskutiert.

Download

C16Unit (Version 1.1) c16unit-1.1.zip (57.07 KB)
Sie müssen angemeldet sein, um die Datei herunterladen zu können.

Keine Kommentare

Kommentar abgeben