Web-Anwendungen effektiv testen

Das Testen von Webapplikationen mit kommunen Unit-Tests ist unzureichend. Denn es muss das ganze Client/Server-System auf Funktionalität und Leistung geprüft werden.

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!



In diesem Artikel werden die Open-Source-Projekte Apache JMeter sowie htmlunit und JWebUnit vorgestellt. Mithilfe dieser und ähnlicher Tools kann man Webanwendungen auf funktionale Aspekte sowie auch auf Performance-Anforderungen hin testen. Diese Tests lassen sich auch vergleichsweise einfach in eine Build-Automatisierung (wie Apache Ant oder Maven) integrieren.


Funktionale- und Last-Tests

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.



Allerdings muss man hier einschränken, dass Last-Tests in der Praxis wesentlich aufwendiger und komplexer sind, als man dies auf den ersten Blick erwarten würde. Zunächst muss der Testaufbau für einen Last-Test geeignet sein. Man benötigt also eigene Maschinen, ein eigenes Netzwerk sowie hinreichend viele Clients, die in der Lage sind, die notwendige Last zu erzeugen. Denn kommt man in die Sättigung eines Services, kann dies verschiedenste Ursachen haben: Die Client-Rechner sind ausgelastet, die Router sind der Flaschenhals oder der Server beziehungsweise die Services am Server sind die Ursache. Daher müssen Last-Tests mit grosser Sorgfalt geplant sowie möglichst alle externen Einflüsse ausgeschaltet werden, um zu einer sinnvollen Interpretation der Ergebnisse zu kommen.


Weiter sind Last-Lests meist auch mit funk-
tionalen Tests zu kombinieren. Feuert man einfach mit einem Tool wie JMeter auf die Startseite der Webanwendung, so wird die Anfrage vielleicht sofort vom schnellen Cache beantwortet und die Ergebnisse sind wertlos.


Apache JMeter

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.


HTML-, HTTP- und JWebUnit

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


Fazit

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.


Der Autor

Alexander Schatten (alexander@schatten.info) ist Assistent am Institut für Softwaretechnik und interaktive Systeme der Technischen Universität Wien.




Artikel kommentieren
Kommentare werden vor der Freischaltung durch die Redaktion geprüft.

Anti-Spam-Frage: Wieviele Zwerge traf Schneewittchen im Wald?
GOLD SPONSOREN
SPONSOREN & PARTNER