Web-Anwendungen effektiv testen
Artikel erschienen in Swiss IT Magazine 2008/20
Die Forderung nach qualitativ hochwertiger und auch wartbarer Software setzt eine enge Verknüpfung von Entwicklung und Tests (wie in der Test-getriebenen Entwicklung) voraus. Testansätze, wie sie manchen noch aus dem Wasserfall-Modell bekannt sind, wo zunächst die Implementierung erfolgt und dann am Ende die Anwendung getestet wird, entsprechen in keinster Weise mehr den Anforderungen an zeitgemässe Software-Entwicklung, insbesondere bei kurzen Release-Zyklen oder Web-Applikationen, die gar keine Versionen mehr im klassischen Sinn kennen.
Auch vereinfachen sie die Entwicklung in Teams, da die Auswirkungen von einzelnen Änderungen über das ganze Projekt einfacher gebändigt werden können. Daher ist es nicht nur wesentlich, dass eine hinreichende Zahl an Tests vorhanden ist, sondern vor allem, dass diese im Rahmen einer Build-Automatisierung (z.B. über Werkzeuge für Continuous Integration) sowie in der IDE des Entwicklers einfach und automatisiert ausgeführt werden können. Dies ist bei Tests niedriger Granularität wie Unit-Tests noch relativ einfach machbar. Möchte man jedoch Anwendungen oder Services, die übers Internet angeboten werden, automatisiert testen, so steigen die Anforderungen an Testumgebung und Tools deutlich an. Auch reichen bei Client/Server-Systemen rein funktionale Tests nicht mehr aus: Es muss gewährleistet werden, dass die Anwendung die Performance-Anforderungen erfüllt!
Zunächst muss man hier zwischen zwei sehr unterschiedlichen Arten von Tests unterscheiden: Bei funktionalen Tests versucht man festzustellen, ob sich die Anwendung verhält, wie man das erwartet. Bei einer Webanwendung muss das Werkzeug automatisiert Seiten aufrufen, Formulare ausfüllen, mit JavaScript interagieren sowie überprüfen können, ob die Ergebnisse den Erwartungen entsprechen.
Bei Last-Test steht zunächst eine andere Frage im Vordergrund: Ist die Webanwendung (oder auch der Webservice) in der Lage, mit der erwarteten Last an Benutzeranfragen umzugehen, und ab wann kann der Server die Anfragen nicht mehr mit hinreichender Geschwindigkeit beantworten? Denn wenn man Load-Balancing-Strategien plant, sollten sie auf ihre Effektivität hin getestet werden.
Apache JMeter erlaubt das Testen beliebiger Webanwendungen. Es unterstützt sowohl funktionale- als auch Last-Tests, wobei JMeter wohl am ehesten bei einer Kombination dieser beiden Tests seine Stärken ausspielen kann. Um einen Test mit JMeter zu definieren, ist es zunächst am einfachsten, die graphische Benutzerschnittstelle zu verwenden. Ein wichtiger Tipp an dieser Stelle: Setzten Sie die Sprache im jmeter.properties auf Englisch (lang=en), denn die deutsche Übersetzung ist nicht gelungen und verwirrt mehr, als dass sie nützt.
Zuerst gilt es, einen Testplan zu definieren. In diesen Testplan können je nach Anforderung des Tests verschiedene Komponenten inkludiert werden. JMeter ermöglicht hier die Definition von Thread Groups. Damit kann definiert werden, wie viele «parallele» Benutzer JMeter simuliert, sowie wie viele Anfragen in Serie ausgeführt werden sollen.
In einem Testplan können unter anderem folgende Komponenten verwendet werden:
- Thread Group
- Sampler (z.B. FTP, HTTP, JMS, LDAP, SOAP)
- Logic Controler (Loop, Switch)
- Listerners (Graph Results, Assertion Results)
- Assertions
- Timers
- Pre-, Post-Processors
- Reports
Die Idee ist es, mit Sampler-Komponenten Zugriffe auf Services zu simulieren. Hier besteht nicht nur die Möglichkeit, HTTP-Requests abzusetzen. Sondern es können auch JMS-Message-Broker, LDAP-Server oder Datenbanken getestet werden. Listener werden eingesetzt, um die Ergebnisse zu verarbeiten, um diese beispielsweise in eine Datei zu schreiben oder Reports in JMeter anzuzeigen. Assertions können verwendet werden, um zu prüfen, ob der Reply vom Server mit den erwarteten Daten übereinstimmt.
Daneben können Pre-Prozessoren verwendet werden, um Daten für den Request anzupassen (z.B. mitgeschickte Parameter, damit für jeden Thread ein eigener Benutzer-Log-in verwendet wird). Post-Prozessoren können die Response-Daten verarbeiten, und mittels Logic Controller lassen sich logische Abläufe an Zugriffen definieren. Die damit erstellten Testpläne können natürlich auch abgespeichert werden.
JMeter bietet ein weiteres interessantes Feature an: Man kann einen Proxy starten, der die Zugriffe des eigenen Browsers auf die zu testende Anwendung mitschreibt. Damit lassen sich initiale Testabläufe erstellen. Man muss hier zwar meist noch händisch nacharbeiten, aber bei komplexeren Abläufen ist dies trotzdem schneller als den gesamten Ablauf in JMeter zu definieren. Tests können auf drei Wegen ausgeführt werden:
- mit dem JMeter-GUI,
- von der Kommandozeile aus sowie
- aus einem Ant-Task oder Maven-Plug-in
Damit kann man JMeter-Tests auch relativ einfach in Test- und Build-Automatisierung integrieren. JMeter ist somit funktional ein sehr umfangreiches Tool. Auch die Dokumentation ist recht vollständig; leider muss man aber sagen, dass die Benutzerführung nicht immer intuitiv ist und JMeter schon etliche «Ecken und Kanten» aufweist. Möchte man eigene Tests erstellen, sollte man sich also Zeit reservieren, um das Tool zu verstehen und auch wirklich sinnvolle Tests mit interpretierbaren Ergebnissen zu erhalten.
Möchte man rein funktionale Tests machen, so können Bibliotheken wie HTML-, HTTP-Unit oder JWebUnit eine gute Wahl sein. Diese lassen sich auch sehr einfach in automatisierte Testumgebungen wie Maven, Continuum oder Hudson integrieren.
Im Gegensatz zu JMeter verfügen diese Bibliotheken nicht über ein GUI, sind aber sehr gut in eigene Java-Testumgebungen zu integrieren. Im Prinzip arbeiten alle drei Werkzeuge ganz ähnlich. Die HTMLUnit-Webseite bezeichnet ihr Tool als «Browser für Java-Anwendungen». Im Zusammenspiel mit Test-Frameworks wie JUnit lassen sich damit recht einfach funktionale Tests von Webanwendungen erstellen. HTMLUnit vereinfacht dabei den Zugriff auf Webseiten (dabei kann z.B. angegeben werden, als welcher Browser man sich zu erkennen geben möchte) und erlaubt das einfache Navigieren in den zurückgesendeten HTML-Dokumenten. Auch die Interaktion mit Formularen ist über die API kein Problem.
JWebUnit setzt eine Schicht höher an und ist ein vollständiges Test-Framework, das wie JUnit arbeitet und dabei auf HTMLUnit zurückgreift. Die Verwendung ist sehr einfach wie Kasten 1 zeigt: In diesem Beispiel wird das «Zahlenraten»-JSP-Beispiel getestet, das mit der Apache-Tomcat-Installation ausgeliefert wird.
Die Struktur des Tests ist genau wie bei JUnit. Im Beispiel sind zwei Testmethoden definiert. JWebunit stellt dabei eine Reihe von Assert-Methoden zur Verfügung, um bestimmte Bedingungen zu prüfen, beispielsweise, ob sich in der Antwort des Servers ein bestimmter Text findet. Auch die Suche im Ergebnis mit XPath ist möglich sowie eine einfache Interaktion mit Formularen oder die Interaktion mit JavaScript, wobei das Rhino Framework als JavaScript-Interpreter eingesetzt wird.
JwebUnit-Beispiel
Wie man sieht, findet sich im Open-Source-Umfeld eine Reihe von Werkzeugen, die das automatisierte Testen von Webanwendungen vereinfachen – wobei Apache JMeter sogar allgemein Anwendungen, die Services über Netzwerkprotokolle anbieten, testen kann. JMeter ist dabei ein Vertreter, der sich vor allem auf die Kombination von funktionalen- und Last-Tests gut anwenden lässt. Der Lernkurve bei JMeter ist allerdings speziell bei Einstieg relativ steil und die Bedienung leider nicht immer intuitiv. Man braucht Zeit, um zu verstehen, was das Werkzeug macht, und um es dazu zu bringen, die Tests in der Weise auszuführen, die zu den Anforderungen passt.
HTML-Unit beziehungsweise JWebUnit sind im Gegensatz dazu sehr schnell und einfach zu verstehen und bieten sich an, wenn man funktionale Tests in Java schreiben möchte, die sich leicht in die anderen Unit-Tests eines Projektes integrieren und sich auch sehr leicht in bestehende Automatisierungs- oder Continuous-Integration-Lösungen einbinden lassen.
Alexander Schatten (alexander@schatten.info) ist Assistent am Institut für Softwaretechnik und interaktive Systeme der Technischen Universität Wien.