Testautomatisierung mit Squish – Teil 2: Fachliche Sicht

Bei einer aktiven Testautomatisierung wächst die Zahl der Testskripte oftmals täglich. Fehlen strukturelle Vorgaben, kann schnell die Übersicht und damit der Mehrwert der Testautomatisierung verloren gehen. Der Wartungsaufwand unstrukturierter Testskripte, die zum Teil nur noch von ihrem einstigen Autor verstanden werden, ist nicht zu unterschätzen und kann unter Umständen den gesamten Projektverlauf negativ beeinflussen.

Unter anderem deswegen wurde Python im ersten Teil als am besten geeignete Skriptsprache für die Testautomatisierung mit Squish bestimmt. Python besitzt wenige (für den Test) unnötige Zeichen und keine komplizierten Klammerungen, wodurch die Testskripte auch für Mitarbeiter ohne Fachkenntnisse gut lesbar sind. Die Klammerung wird in Python durch Zeileneinrückungen ersetzt, die immer aus vier Leerzeichen bestehen. Dies erhöht die Lesbarkeit des Testcodes zusätzlich.

Gerade in der Medizintechnik sind Testskript-Reviews ab und an erforderlich und seit neuestem sogar gefordert. Gut strukturierte Testskripte reduzieren den Zeitaufwand solcher Reviews enorm. Auch bei der Test-Fehleranalyse macht sich ein sauber aufgebautes Test-Framework positiv bemerkbar. Wie baut man also seine Testskripte auf?

Bei unserem Kunden hat sich die Vorgehensweise bewährt, zu allererst einen manuell durchführbaren Testfall mittels Testmanagement-Tool zu erstellen, so dass dieser per manuell geführtem Mauszeiger an der AUT („Application Under Test“) in der jeweiligen Testumgebung ohne Squish durchführbar wäre. Danach wird mit der Implementierung des Testskripts begonnen. Nicht nur aus Review-Sicht ist es hier empfehlenswert, die Testfall-ID gleichzeitig als Name des Testskripts zu verwenden (d.h. Testskript “tst_1134” würde Testfall mit ID 1134 automatisieren).

Die Testskripte an sich basieren auf einem Template, das im groben folgende Struktur aufweist:

tstHelper = TstHelper()
 
def main(): 
    try:
        preconditions()
        testprocedure()
        cleanUp()
    except Exception, e:
        tstHelper.fatal(e)
 
def preconditions():
    'Vorbedingungen des Testfalls'
      
def testprocedure():
    'Testschritte und erwartete Ergebnisse'
        
def cleanUp():
    'AUT in den Ausgangszustand versetzen'

Wie in Teil 1 beschrieben wurde, genügt ein Einzeiler am Anfang, um die AUT zu starten bzw. sich mit dem laufenden Prozess zu verbinden, indem einfach ein Objekt der Klasse TstHelper instanziiert wird.  Weiterhin auffällig ist, dass sämtliche Vorbedingungen und Testschritte innerhalb eines try-except-Blocks abgehandelt werden. Alle unvorhergesehenen Exceptions, die während einer sauberen Testdurchführung nicht auftreten dürfen, werden abgefangen um eine individuelle Fehlerbehandlung zu starten.

Um einen fehlerhaften Zustand der AUT nicht zum Ausgangspunkt für nachfolgende Tests zu machen, sollte die Fehlerbehandlung darin bestehen, die AUT zu beenden und die Testumgebung aufzuräumen. So wird unter anderem vermieden, den Fehler mitzuschleifen und “False-Negative”-Testergebnisse bei nachfolgenden Tests zu erzeugen.

In der Methode “preconditions()” sind sämtliche, im manuellen Testfall beschriebenen Vorbedingen unterzubringen. Um die Wartbarkeit hoch zu halten, empfiehlt es sich, für jede Vorbedingung eine eigene Testskriptfunktion zu erstellen und diese so zu benennen, dass deren Name eindeutig deren funktionelle Inhalte widerspiegelt. Vorbedingung “User A ist ausgewählt” wird beispielsweise zur Testskriptfunktion “selectUser(“User A”)”, “Menü zur Konfiguration des Lichts ist geöffnet” wird zu “gotoConfigMenuLight()” usw.

Analog dazu wird mit Methode “testprocedure()” verfahren. Sie wird später alle Testschritte und erwarteten Ergebnisse enthalten. Zur besseren Strukturierung kann den einzelnen Testschritten zusätzlich ein kurzer Kommentar vorangestellt werden (z.B. “# 1.”). Auch hier sollte wieder, soweit möglich, pro Aktion im Testfall eine Testskriptfunktion geschrieben werden. Die Prüfung des jeweils erwarteten Ergebnisses sollte allerdings nicht innerhalb dieser Funktion, sondern separat aufgeführt gut lesbar im aufrufenden Testskript stehen. Dies erleichtert ein Review enorm und wirkt sich zudem positiv auf die Wartbarkeit der Skripte aus.

Eine 1:1-Beziehung zwischen Testskriptfunktion und Testschritt ist nicht immer möglich. Darum ist es auch zulässig, mehrere Funktionen im Testskript zu Blöcken zusammenzufassen und die Relation zum zugrundeliegenden Testschritt mittels passender Kommentierung herzustellen.

Ein großer Vorteil dieser Herangehensweise ist die automatische Trennung von technischer und fachlicher Testebene. Es entsteht ganz automatisch eine Bibliothek aus Testskriptfunktionen, die bei der Erstellung weiterer Testskripte wiederverwendet wird. Allerdings kann diese Bibliothek auch selbst wieder (technische) Fehler aufweisen. Häufig passiert es, dass Fehler bei der Testdurchführung nicht auf fachlicher sondern auf technischer Ebene zu suchen sind. Fehlerhaft implementierte Testskriptfunktionen können schnell zu “False-Negative”- bzw. “False-Positive”-Testergebnissen führen.

Um dem entgegenzuwirken, sollte eine eigene Testsuite erstellt werden, die ausschließlich die Bibliothek der Testskriptfunktionen im Sinne eines Unit-Tests prüft. Jede Testskriptfunktion sollte mindestens einmal auf alle möglichen Ein- und Ausgabewerte geprüft werden. Soweit möglich sollten die Tests wenige Fachlichkeiten enthalten und im Grunde genommen immer erfolgreich durchlaufen. Die AUT wird dabei nicht direkt getestet, sondern dient nur als Mittel zum Zweck, den Unit-Tests der Testskriptfunktion die passende Testumgebung zur Verfügung zu stellen.

Es empfiehlt sich zudem, diese Testsuite stets vor den eigentlichen produktiven Tests laufen zu lassen. Werden Fehler festgestellt, muss dies zum Abbruch der gesamten GUI-Testautomatisierung führen. So wird verhindert, dass technische Fehler in den Testskriptfunktionen “False-Negative”- bzw. “False-Positive”- Testergebnisse generieren, die wiederum einen erhöhten Aufwand bei der Fehleranalyse mit sich bringen würden (falls sie überhaupt gefunden werden).

Zusammenfassend kann gesagt werden, dass durch den hohen Funktionsumfang von Python nahezu jeder GUI-Test mit Squish automatisiert werden kann. Ein großer Pluspunkt ist zudem der hervorragende Support von Froglogic. Es ist selten vorgekommen, dass der Support länger als einen Tag zum Beantworten einer Anfrage gebraucht hat. Um den vollen Funktionsumfang von Squish ausschöpfen zu können, sind jedoch Programmiergrundkenntnisse zwingend erforderlich.

Steve Gronwaldt

Steve Gronwaldt ist seit 2007 im Fachbereich Software-Qualitätssicherung der Saxonia Systems AG tätig. Während dieser Zeit sammelte er Erfahrungen in klassischen sowie agilen Projekten im industriellen und medizinischen Umfeld. Neben Testanalyse und Testdesign umfasst sein Tätigkeitsfeld dabei vor allem Testautomatisierungs- und Testmanagement-Aufgaben unter Berücksichtigung gültiger Normen. Besonderes Interesse hegt er an der Schittstelle zwischen SW-Entwicklung und Test und den damit verbundenen technischen Möglichkeiten zur Automatisierung von manuellen Abläufen.