Testautomatisierung mit Squish – Teil 1: Technische Sicht

Am Markt existiert mittlerweile eine Vielzahl von Testautomatisierungswerkzeugen für die verschiedensten Einsatzgebiete. Bei einem unserer Kunden aus der Medizintechnik wurde beispielsweise das Tool „Squish“ intensiv zur Automatisierung von GUI-Tests eingesetzt. In diesem Beitrag möchte ich deshalb näher auf die dabei zu beachtenden technischen und fachlichen Aspekte beim Design des Testframeworks und der Testskripte eingehen. Auch im zweiten Teil der Blogpost-Reihe gibt es mehr darüber zu erfahren.

Beim GUI-Testautomatisierungstool “Squish” der Hamburger Softwareschmiede Froglogic wird der gesamte Testcode mit allem, was dazu gehört, in einer von mittlerweile fünf gängigen Programmier- bzw. Skriptsprachen verfasst und verwaltet. Neben Ruby und JavaScript stehen hier Tcl und Perl zur Auswahl. Aufgrund seiner Aktualität und seines mächtigen Funktionsumfangs, der durch die zahlreichen, frei erhältlichen Libs noch erhöht werden kann, aber vor allem wegen der ausgesprochen guten Lesbarkeit der damit erzeugten Testskripte, sollte Python das Mittel zur Wahl werden. Standardmäßig wird Squish mit einem Python 2.7.XX ausgeliefert, auf individuelle Nachfrage stellt Froglogic allerdings auch gern eine Squish-Edition mit der gewünschten Python-Version (z.B. Python 3.5.XX) zum Download bereit. Wer auf Python 3 schwört, kann diese Möglichkeit gern in Betracht ziehen, mit dem mitgelieferten Python 2.7 ist man jedoch bestens bedient.

Abbildung 1: Squish IDE basiert auf der Open-Source IDE Eclipse

Unabhängig von der gewählten Skriptsprache bietet Squish generell zwei Ansätze für den Umgang mit einer AUT (“Application Under Test”) zur Testdurchführung. Entweder die AUT wird für jeden Testfall durch Squish implizit gestartet und gestoppt oder und man verbindet sich für jeden Testfall neu zu einer bereits laufenden AUT. Da die meisten Softwareanwendungen in der Praxis nicht fortlaufend beendet und neu gestartet werden, kommt der letzte Ansatz einem Verhalten in der Realität näher und ist ersterem unbedingt vorzuziehen.

In der Squish-Welt wird dieser Ansatz auch als “Attachable AUT” bezeichnet. Die Testskriptfunktionen zum Steuern einer “Attachable AUT” werden von Froglogic jedoch nur zum Teil zur Verfügung gestellt und müssen selbst implementiert werden.

Über Jahre bewährt hat sich bei unserem Kunden hier der “TstHelper”. Wie der Name andeutet, handelt es sich dabei um eine Helferklasse für die Testdurchführung, die u.a. einen Mechanismus zum Handling des “Attachable AUT”-Ansatzes implementiert. Um überflüssigen Testcode in einem Testskript zu minimieren, wurde der gesamte Mechanismus im Konstruktor untergebracht. Ein Einzeiler, der ein Objekt der Klasse “TstHelper” zu Beginn eines Testskripts instanziiert, ist somit ausreichend – dazu mehr im zweiten Teil.

Im Prinzip besteht der Mechanismus nur aus einem einzigen “try-except”-Block:

try:
     attachToApplication()
except RuntimeError:
     AppUnderTest.start() 

Ein “RuntimeError” wird von der Squish-Funktion “attachToApplication” genau dann geworfen, wenn sich zu einer AUT verbunden werden soll, die noch nicht gestartet wurde. Dann wird die statische Funktion AppUnderTest.start() aufgerufen, die – wie der Name schon vermuten lässt – die AUT startet. Sowohl die Klasse als auch die Funktion müssen selbst implementiert werden. Der Name “AppUndertest” sollte durch den Namen der tatsächlich zu testenden Applikation ersetzt werden. Dieser Name ist dann zugleich der Name des Namespace, der die Funktion start() bereitstellt.

In Python existiert keine eigene Notation für Namespaces, weshalb Namespaces mittels Klassen realisiert werden. Vereinfacht gesehen sollte diese Klassenstruktur folgendermaßen aussehen:

class AppUnderTest:
    
    @staticmethod
    def start():
         os.system("{BatchSkript}")
     
    @staticmethod
    def stop():
         ToplevelWindow.byName("{MainWindowObjID}", 10).close()

Beim “Attachable AUT”-Ansatz läuft die AUT in einem eigenen Prozess unabhängig von Squish. Das Starten erfolgt daher über ein externes Batch-Skript, das einmalig zu erstellen ist. Die Einbindung des Skript-Aufrufs erfolgt dann in der start()-Funktion mittels des Python-Kommandos “os.system” (s.o.).

Zum Stoppen der AUT bringt Squish die Funktion “ToplevelWindow.byName(“{MainWindowObjID}”, 10).close()” mit. Der Parameter “MainWindowObjID” repräsentiert dabei die Objekt-ID des hierarchisch gesehen obersten Elements aus der Object-Map. Der Funktionsaufruf wird in der statischen Funktion stop() gekapselt. Ihrem Aufruf im Testskript muss demzufolge ebenfalls der Klassenname vorangestellt werden: AppUnderTest.stop(). Diese Syntax wurde bewusst gewählt wegen ihrer guten und eindeutigen Lesbarkeit. Alle Funktionen, die in Verbindung mit der AUT stehen, sollten in dieser Klasse bzw. diesem Namespace zusammengefasst werden. Es können Funktionen ergänzt werden, um etwa die AUT in ihren Ausgangszustand zurückzuversetzen, auf bestimmte System-Ereignisse zu warten bzw. zu reagieren oder den “attachToApplication()”-Aufruf zu kapseln, um eventuell Logging hinzuzufügen.

Die Organisation in Namespaces eignet sich zudem ideal zur Einbindung zusätzlicher Testtools, die aus einem Testskript heraus gesteuert werden sollen. Für jedes Testtool wird eine eigene Python-Klasse nach obigem Schema erstellt. In den start()- und stop()-Methoden ist jeweils der Aufruf zum Starten und Stoppen des Testtools unterzubringen. Auch diese Methodenliste kann beliebig erweitert werden, z.B. um Funktionen zum Sichern der Logfiles etc. Im Testskript erfolgt deren Aufruf dann analog mittels “Testtool.start()” bzw. “Testtool.saveLogfilesTo()”. Ich denke, es muss nicht erwähnt werden, dass der Klassenname durch den Namen des Testtools ersetzt werden sollte. Dadurch ergibt sich eine Syntax wie beispielsweise “CanSimuator.start()”, was zur erhöhten Lesbarkeit und somit zu einem erleichterten Testskript-Review beiträgt – mehr dazu im zweiten Teil der Blogpost-Reihe.

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.