Perl-Programmierung: Testen
Perl-Module und auch manchmal Applikationen werden am Besten mit automatischen Testskripts ausgeliefert. Diese sollen sicherstellen, dass bei späteren Änderungen an den Modulen keine Fehler auftreten.
Besonders in Perl gibt es eine große Kultur rund um das Testen. Dies zeigt sich im CPAN Testers-System, das auch gut ins CPAN eingebunden ist. Teilnehmer stellen dabei Rechner bereit, die Perl-Module automatisch testen und die Ergebnisse der Testskripte im Internet bereitstellen.
Damit Testskripte effektiv sind, müssen sie die gesamte Funktionalität des Programms abdecken. Wie geeignete Softwaretests geschrieben werden können, soll nicht in diesem Buch abgedeckt werden (dazu finden sich Informationen z.B. in der Wikipedia: w:Softwaretest). Vielmehr sollen hier die Methoden beschrieben werden, die Perl dem Programmierer für Softwaretests bereitstellt.
Konzept
[Bearbeiten]Was Testskripte tun
[Bearbeiten]Testskripte werden seltenst manuell ausgeführt. Diese Aufgabe wird an einen Testtreiber abgegeben, der die Testskripte automatisch aufruft und die Ergebnisse auswertet. Bei Fehlern oder Unstimmigkeiten wird der Benutzer darüber informiert, und (je nach Treiber unterschiedlich ausführliche) Informationen über den gescheiterten Test werden ausgegeben.
Technisch unterhalten Testskripte sich über ein einfaches, zeilenbasiertes Format. Trotzdem geschieht die Kommunikation meist über darauf spezialisierte Module, die dann im Fehlerfall automatisch zusätzliche Informationen ausgeben können.
Grundsätzlich meldet ein Testskript eine bestimmte Zahl von auszuführenden Tests an und meldet dann für jeden Testfall Erfolg oder Misserfolg. Jeder Fall kann dabei mit einem beschreibenden Text versehen werden. Die Zahl der Tests sorgt dafür, dass ein Fehler erkannt werden kann, falls ein Testskript vorzeitig abstürzt.
Wo Testskripte abgelegt werden
[Bearbeiten]TBA
Module
[Bearbeiten]Wie so üblich in Perl gibt es viele verschiedene Module für Softwaretests, einige davon sollen hier vorgestellt werden. All diese sind in einer Standard-Perl-Installation bereits dabei.
Test::Simple
[Bearbeiten]Das wohl simpelste Testmodul ist Test::Simple. Es bietet genau eine Funktion an: ok($Bedingung, $Beschreibung). Ist $Bedingung ein wahrer Ausdruck, gilt der Test als bestanden, sonst nicht.
Test::Simple erwartet vom Programmierer die Zahl der auszuführenden Tests. Diese muss zuvor bereits bekannt sein und wird hinter die use-Zeile geschrieben (siehe Beispiel).
Die meisten Testprogramme zeigen den als $Beschreibung angegebenen Text an, wenn der Test nicht bestanden ist, um die Fehlersuche zu erleichtern. Eine passende Beschreibung könnte sein: "module loads ok".
Beispiel
[Bearbeiten]# Zahl der auszufuehrenden Tests, hier 2
use Test::Simple tests => 2;
# Die Testfaelle
ok((2 * 6) == 12, "zwei mal sechs ist zwoelf");
ok(reverse("foobar") eq "raboof", "'foobar' rueckwaerts ist 'raboof'");
Test::More
[Bearbeiten]Für gehobene Ansprüche steht Test::More zur Verfügung. Es stellt zahlreiche Funktionen bereit, um häufige Testfälle abzudecken. Testskripte, die zuvor mit Test::Simple erstellt wurden, laufen stets mit Test::More, sodass ein Austausch der use-Zeile genügt.
Vergleiche
[Bearbeiten]Bei Vergleichen ist es oft hilfreich, zu wissen, welcher Wert tatsächlich zurückgegeben wurde. Das von Test::Simple bekannte ok kann davon allerdings nichts wissen. Dafür gibt es is($Tatsaechlicher_Wert, $Erwarteter_Wert, $Beschreibung) und isnt. Der $Tatsaechliche_Wert ist der Rückgabewert der zu testende Funktion und $Erwarteter_Wert das erwartete Ergebnis (oder, im Falle von isnt, das unerwartete Ergebnis):
is(2 * 6, 12, "zwei mal sechs ist zwoelf");
isnt(funktion(), 0, "Funktionswert nicht null");
Ähnlich verwendet werden like und unlike, die den erwarteten Wert als regulären Ausdruck auf den tatsächlichen Wert anwenden:
like("Testdaten", qr/[aeiou]/i, "Testdaten enthalten mindestens einen Vokal");
Komplexe Datenstrukturen würden normalerweise eine lange Kette von Vergleichen nach sich ziehen. Eine starke Arbeitserleichterung ist is_deeply: Wie is vergleicht es die beiden ersten Argumente, allerdings als Datenstruktur und nicht nur als Skalar. (Bei is würden die Datenstrukturen in Speicheradressen umgewandelt werden, die immer verschieden sind.) Dazu direkt ein Beispiel:
my $Erwartete_Daten = [
{ name => "Max Mustermann", adresse => "..." },
{ name => "Erika Mustermann", adresse => "..." },
];
my $Tatsaechliche_Daten = lese_adressbuch("testdaten1.adr");
is_deeply($Tatsaechliche_Daten, $Erwartete_Daten, "Adressbuch korrekt ausgelesen");
Modul-Tests
[Bearbeiten]Für Modulautoren ist sicherlich interessant, vor allen anderen Tests erst einmal zu prüfen, ob das Modul überhaupt geladen werden kann - ist das nicht der Fall, sind alle weiteren Tests sinnfrei. Dazu existiert die Funktion require_ok($Modulname), die das einfach und unkompliziert erledigt. Alternativ kann use_ok verwendet werden, allerdings muss solch ein Aufruf dann in einem BEGIN-Block getätigt werden.
Zahl der geplanten Tests später berechnen
[Bearbeiten]Test::More erlaubt es im Gegensatz zu Test::Simple, keine bestimmte Zahl von Tests festzulegen. Dies muss allerdings explizit angegeben werden:
use Test::More 'no_plan';
Vielleicht muss die Zahl der auszuführenden Tests aber auch erst berechnet werden. Hier werden beispielsweise Tests mit Testdaten aus einem Verzeichnis ausgeführt, das erst noch eingelesen werden muss. Anschließend werden pro Datei zehn Bedingungen überprüft:
use Test::More;
opendir(DIR, "testdaten");
my @Dateien = grep { /^[^.]/ } readdir(DIR);
closedir(DIR);
plan tests => @Dateien * 10;
Tests überspringen
[Bearbeiten]Es kann vorkommen, dass manche Tests zusätzliche Software benötigt, die nicht überall installiert ist. Oft ist diese Software auch gar nicht zwingend zur Benutzung des Moduls erforderlich, sodass es wenig Sinn hat, dieses Modul als Abhängigkeit aufzuführen. Oder bestimmte Tests können unter bestimmten Betriebssystemen nicht ausgeführt werden.
Damit die Tests in diesem Fall nicht mit einem Fehler abgebrochen werden müssen, können Tests übersprungen werden, wenn beispielsweise Module nicht installiert sind. Dies wird mit der Funktion skip erledigt. Als Argumente werden ein beschreibender Text und die Zahl der zu überspringenden Tests angegeben.
if ($^O ne "MSWin32") {
skip("<FUNKTION> unter Windows nicht verfuegbar und kann nicht getestet werden", 4);
} else {
# hier vier Tests
}