Ausarbeitung - Institut für Wirtschaftsinformatik der WWU Münster ...
Ausarbeitung - Institut für Wirtschaftsinformatik der WWU Münster ...
Ausarbeitung - Institut für Wirtschaftsinformatik der WWU Münster ...
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
Westfälische Wilhelms-Universität <strong>Münster</strong><br />
<strong>Ausarbeitung</strong><br />
Testen von Webanwendungen mit<br />
HttpUnit und Cactus<br />
im Rahmen des Seminars „Ausgewählte Themen des Softwareengineering“<br />
Themensteller: Prof. Dr. Herbert Kuchen<br />
Betreuer: Prof. Dr. Herbert Kuchen<br />
<strong>Institut</strong> <strong>für</strong> <strong>Wirtschaftsinformatik</strong><br />
Praktische Informatik in <strong>der</strong> Wirtschaft<br />
Volker Frehe
Inhaltsverzeichnis<br />
1 Einleitung................................................................................................................... 3<br />
2 Grundlagen des Testens............................................................................................. 4<br />
2.1 Ausgewählte Arten von Testverfahren ............................................................. 4<br />
2.2 Testen mit JUnit................................................................................................ 4<br />
3 Testen von Webanwendungen................................................................................... 6<br />
3.1 Testen mit HttpUnit .......................................................................................... 6<br />
3.1.1 Aufbau einer Verbindung ............................................................................. 7<br />
3.1.2 Ausgewählte Methoden von HttpUnit zum Extrahieren von Elementen ..... 7<br />
3.1.3 Auswertung <strong>der</strong> Ergebnisse ........................................................................ 10<br />
3.2 Testen mit Jakarta Cactus ............................................................................... 12<br />
3.2.1 Generelle Funktionsweise von Jakarta Cactus............................................ 13<br />
3.2.2 Testen von Servlets..................................................................................... 14<br />
3.2.3 Testen von Servlet-Filtern .......................................................................... 15<br />
3.2.4 Testen von JSPs .......................................................................................... 16<br />
4 Zusammenfassung und Fazit ................................................................................... 17<br />
Literaturverzeichnis ........................................................................................................ 18<br />
II
Kapitel 1: Einleitung<br />
1 Einleitung<br />
Testen ist eine <strong>der</strong> wichtigsten Stufen bei <strong>der</strong> Entwicklung von Software. Fehler, die<br />
erst im fertigen Produkt aufgefunden werden, können meist gar nicht o<strong>der</strong> nur unter<br />
enormen Kosten nachträglich behoben werden. Gerade unter diesem Kostenaspekt wird<br />
dem Testen eine immer bedeuten<strong>der</strong>e Rolle zugewiesen und Ausreden wie „Ich habe<br />
keine Zeit zum Testen“ o<strong>der</strong> „Ich programmiere fehlerfrei“ sind nicht mehr akzeptabel.<br />
Somit ist das Testen nicht nur auf die reine Testabteilung beschränkt, son<strong>der</strong>n wird<br />
immer mehr in die Hände des Programmierers gelegt, um frühzeitig Fehler zu erkennen<br />
und zu beheben.<br />
Bei <strong>der</strong> Entwicklung von Webanwendungen kommt dem Testen noch eine weitere<br />
wichtige Bedeutung zu. Da die Anwendung nicht nur rein firmenintern läuft, son<strong>der</strong>n<br />
weltweit abgerufen werden kann, muss sichergestellt werden, dass die Anwendung auch<br />
auf den verschiedenen Browsern und Systemen fehlerfrei funktioniert.<br />
Um die Kosten und den Aufwand <strong>für</strong> die Tests in einem angemessenen Rahmen zu<br />
halten, bietet es sich an, die Tests nicht manuell durchzuführen, son<strong>der</strong>n hier<strong>für</strong> eine<br />
geeignete Software zu benutzen und die Tests zu automatisieren. Dies hat den Vorteil,<br />
dass zum einen die Tests wesentlicher schneller durchgeführt werden können. Des<br />
Weiteren sind die Tests so reproduzierbar und können nach einer Än<strong>der</strong>ung des Codes<br />
mit denselben Daten, welche zum Beispiel in einer Datenbank abgelegt werden, erneut<br />
ausgeführt werden.<br />
Ziel dieser Arbeit ist es, zwei Frameworks, nämlich HttpUnit und Jakarta Cactus,<br />
welche beide eine Erweiterung des Programms JUnit sind, <strong>für</strong> das Testen von<br />
Webanwendungen vorzustellen. Hierzu werden zuerst in Kapitel 2 verschiedene<br />
ausgewählte Testarten vorgestellt. Kapitel 3 enthält einen Überblick über die<br />
Funktionsweise von HttpUnit und Jakarta Cactus. In Kapitel 4 wird ein kurzer<br />
Vergleich <strong>der</strong> beiden Programme gegeben und erläutert, <strong>für</strong> welche Arten von<br />
Webanwendungen sie geeignet sind.<br />
3
Kapitel 2: Grundlagen des Testens<br />
2 Grundlagen des Testens<br />
2.1 Ausgewählte Arten von Testverfahren<br />
Das Testen von Software kann in verschiedenster Weise und zu verschiedenen<br />
Zeitpunkten <strong>der</strong> Software-Entwicklung vorgenommen werden. In Anlehnung an<br />
[KWTRF06] werden hier vier wichtige Testarten beschrieben. In Unit-Tests werden<br />
einzelne Komponenten <strong>der</strong> Anwendung, wie zum Beispiel Klassen o<strong>der</strong> Methoden,<br />
getestet. Durch Integrationstests wird anschließend geprüft, ob diese Komponenten mit<br />
dem Rest des Systems einwandfrei funktionieren. Der funktionale Test wird<br />
ausgeführt, um zu testen, ob das gesamte System entsprechend seinem<br />
Verwendungszweck einwandfrei arbeitet. Stresstests sollen das Verhalten des fertig<br />
entwickelten Systems unter starker Belastung überprüfen, um einen fehlerfreien Betrieb<br />
im Extremfall zu Gewähr leisten.<br />
Eine weitere Unterscheidung kann in so genannte Glass-Box- und Black-Box-Tests<br />
erfolgen. Bei Glass-Box-Tests ist dem Tester <strong>der</strong> komplette Quellcode bekannt,<br />
wohingegen <strong>der</strong> Tester bei Black-Box-Tests lediglich das fertige Programm und nicht<br />
den Quellcode selber sieht.<br />
Dies ist natürlich nur eine Auswahl von verschiedenen Testarten. Oft lassen sich Tests<br />
auch nicht in eine einzelne Kategorie einordnen, son<strong>der</strong>n stimmen mit mehreren<br />
Definitionen überein.<br />
2.2 Testen mit JUnit<br />
JUnit ist eine Software, um Unit-Tests <strong>für</strong> Java-Anwendungen zu schreiben. Die<br />
Testfälle sind selber in Java geschriebene Klassen, welche die zu testenden Methoden<br />
aufrufen und ihre zurück gelieferten Werte überprüfen. Diese Klassen haben alle die<br />
JUnit Klasse TestCase als Superklasse, um bestimmte bereitgestellte Methoden zum<br />
Testen benutzen und gegebenenfalls erweitern zu können.<br />
Um die zu testenden Methoden zu überprüfen, bietet JUnit einige Hilfsmethoden, die so<br />
genannten asserts, an. Die Methoden sind so aufgebaut, dass als erster (optionaler)<br />
Parameter eine Meldung angegeben wird, die bei Fehlschlag angezeigt wird. Die<br />
weiteren Parameter sind die Konditionen, die zu vergleichen sind. Eine oft benutzte<br />
4
Kapitel 2: Grundlagen des Testens<br />
assert-Methode ist assertEquals ([String message,] expexted, actual).<br />
Hiermit können die beiden Parameter expected und actual auf Übereinstimmung<br />
getestet werden. Die Methode assertTrue ([String message,] boolean<br />
condition) überprüft einen Ausdruck und gibt die Fehlermeldung aus, wenn er nicht<br />
wahr ist [Li05].<br />
Um einen Test auszuführen, müssen oft auch weitere Parameter, die nicht durch die zu<br />
testende Methode festgelegt werden, vorher auf einen bestimmten Wert festgelegt<br />
werden o<strong>der</strong> Verbindungen aufgebaut werden. Hierzu bietet JUnit die beiden Methoden<br />
setUp() und tearDown() an. Diese können in den Testklassen überschrieben werden<br />
und werden jeweils vor dem Test bzw. nach dem Test ausgeführt. Die Tests selber<br />
werden von JUnit eigenständig erkannt, sofern sie als testXXX() benannt sind [Li05].<br />
Die fertigen Testfälle werden dann mit dem JUnit TestRunner ausgeführt. Diesen gibt<br />
es zum einen als textbasierte Benutzeroberfläche, aber auch als grafische Variante. Bei<br />
<strong>der</strong> grafischen Variante wird mittels eines Balkens, entwe<strong>der</strong> rot <strong>für</strong> einen Fehlschlag<br />
o<strong>der</strong> grün <strong>für</strong> den Erfolg, das Ergebnis des Testens dargestellt. Außerdem gibt er die<br />
Fehlermeldung (wenn angegeben) aus.<br />
Abbildung 1: Erfolgreicher JUnit-Test [Ju07]<br />
5
Kapitel 3: Testen von Webanwendungen<br />
Abbildung 2: Fehlgeschlagener JUnit-Test [Ju07]<br />
Durch diese einfachen Methoden und die übersichtliche Auswertung lassen sich schnell<br />
Testfälle entwickeln, welche parallel zum zu entwickelnden Projekt ausgeführt werden<br />
und somit zu einer fehlerfreien Programmierung führen.<br />
3 Testen von Webanwendungen<br />
HttpUnit und Jakarta Cactus stellen keine eigenständige Testsoftware dar, son<strong>der</strong>n sind<br />
lediglich Erweiterungen, welche auf JUnit aufbauen. Wie schon <strong>der</strong> Name sagt, lassen<br />
sich mit JUnit Unit-Tests durchführen, was jedoch bei HttpUnit und Jakarta Cactus<br />
nicht <strong>der</strong> Fall ist. Beide Erweiterungen stellen Funktionen und Schnittstellen zur<br />
Verfügung, um Webanwendungen zu testen. Bei HttpUnit handelt es sich um (Black-<br />
Box) funktionale Tests und bei Jakarta Cactus um so genannte (White-Box)<br />
Integrations-Tests.<br />
3.1 Testen mit HttpUnit<br />
Um Webanwendungen zu testen, verfügt HttpUnit über eine API sowie diverse<br />
Methoden, um Anfragen an einen Server zu senden und Antworten von ihm zu<br />
empfangen. HttpUnit stellt somit eine Schnittstelle zwischen dem Testprogramm<br />
(JUnit) und dem Http-Protokoll dar, in dem es ausgewählte Eigenschaften eines<br />
Browsers simuliert, wodurch das Senden und Empfangen von Http-Anfragen<br />
ermöglicht wird. Des Weiteren bietet HttpUnit Methoden, um Formulare mit<br />
6
Kapitel 3: Testen von Webanwendungen<br />
entsprechenden Daten zu füllen und abzusenden, Links zu verfolgen, aber auch Cookies<br />
zu empfangen und Teile aus den zurück gelieferten Daten zu extrahieren.<br />
3.1.1 Aufbau einer Verbindung<br />
Um eine Verbindung mit dem Server aufzubauen und einen Request zu senden, wird<br />
zuerst mittels eines erzeugten WebConservation Objektes eine Sitzung gestartet. Die<br />
Klasse WebRequest enthält nun Methoden, mit <strong>der</strong>en Hilfe es möglich ist, eine Anfrage<br />
inklusive <strong>der</strong> URL zu <strong>der</strong> zu testenden Anwendung sowie die erfor<strong>der</strong>lichen Parameter<br />
an den Server zu senden. Die Antwort kann danach in einem Objekt WebResponse<br />
empfangen werden und enthält sowohl die Seite selber als auch Metadaten wie Cookies.<br />
Im folgenden Beispiel wird nun eine neue Verbindung mit dem Internet hergestellt, eine<br />
Anfrage gesendet und das Ergebnis empfangen [HOVPG04, S. 300]:<br />
WebConversation conv = new Webconversation();<br />
WebRequest req = new GetMethodWebRequest(“http://www.wi.unimuenster.de/”);<br />
WebResponse response = conv.getResponse(req);<br />
Der Code lässt sich noch weiter abkürzen, in dem man die URL direkt in den<br />
WebResponse einbaut und den WebRequest komplett weglässt:<br />
WebConversation conv = new Webconversation();<br />
WebResponse response = conv.getResponse(“http://www.wi.unimuenster.de/”);<br />
Zu beachten ist hier, dass dies lediglich eine Anfrage an den Server und Antwort von<br />
diesem ist. Eine Auswertung <strong>der</strong> zurück gelieferten Antwort findet hierbei noch nicht<br />
statt.<br />
3.1.2 Ausgewählte Methoden von HttpUnit zum Extrahieren von<br />
Elementen<br />
Zur Auswertung <strong>der</strong> zurück gelieferten Seiten stehen dem Programmierer verschiedene<br />
Klassen mit diversen Methoden zur Verfügung, um Teile aus <strong>der</strong> Antwort zu separieren<br />
und diese mit den JUnit assert-Methoden zu überprüfen.<br />
7
Kapitel 3: Testen von Webanwendungen<br />
Abbildung 3: Ausgewählte Klassen von HttpUnit [Fe04]<br />
Eine Möglichkeit ist, aus dem empfangenen Ergebnis die übermittelten Links<br />
herauszufiltern. Dies funktioniert mit getLinkWith(“string”), wobei <strong>der</strong> string <strong>der</strong><br />
klickbare Text und nicht die URL des Links ist. Zum einen kann hiermit geprüft<br />
werden, ob ein gewünschter Link auch in <strong>der</strong> Seite enthalten ist, zum an<strong>der</strong>en ist es aber<br />
auch möglich, diesen Link zu verfolgen und eine neue Anfrage mit diesem Link an den<br />
Server zu schicken. Will man zum Beispiel über eine Start-Seite zu einer<br />
weiterführenden Seite gelangen, <strong>der</strong>en Link als Text „Weitere Infos“ enthält, erreicht<br />
man dies mit folgendem Code [HOVPG04, S. 300]:<br />
WebConversation conv = new Webconversation();<br />
WebResponse response = conv.getResponse(“http://www.wi.unimuenster.de/”);<br />
WebLink link = response.getLinkWith(“Weitere Infos”);<br />
WebRequest request = link.getRequest();<br />
response = conv.getResponse(request);<br />
Da Webanwendungen meist aus Formularen bestehen, die Eingaben erfor<strong>der</strong>n, stellt<br />
HttpUnit auch Methoden bereit, um Formulardaten zum einen auszufüllen aber auch um<br />
das Formular schließlich abzuschicken. Als Beispiel sei hier eine kleine Anwendung<br />
8
Kapitel 3: Testen von Webanwendungen<br />
gezeigt, welches ein Loginscript mit Usernamen und Passwort enthält: [HOVPG04, S.<br />
302 f.]:<br />
Abbildung 4: Html-Seite mit Login-Formular<br />
WebConversation conv = new Webconversation();<br />
WebResponse response = conv.getResponse(“http://www.wi.unimuenster.de/”);<br />
WebForm form = response.getForms()[0];<br />
WebRequest loginreq = form.getRequest();<br />
loginreq.setParameter (“username”, user);<br />
loginreq.setParameter (“password”, pass);<br />
WebResponse loginresponse = conv.getResponse(loginreq);<br />
Die ersten beiden Zeilen des Codes sind bekannt. Mit Zeile drei wird nun das erste<br />
Formular <strong>der</strong> Antwort ausgewählt. Mit dem ausgewählten Formular kann man nun<br />
einen neuen Request an den Server formulieren, welches in Zeile vier geschieht. Zeile<br />
fünf und sechs weisen den Parametern username und password noch die entsprechenden<br />
Werte zu, um schließlich in Zeile sieben die Anfrage abzusenden und die Antwort zu<br />
erhalten.<br />
Während die ersten beiden Beispiele dazu dienten, von einer zur nächsten Seite zu<br />
navigieren, bietet die Klasse WebResponse auch Methoden, um Teile aus <strong>der</strong> <strong>der</strong><br />
Antwort des Servers zu extrahieren. Als Beispiel seien hier einige Methoden genannt,<br />
mit denen es möglich ist, spezielle Tabellen auszuwählen:<br />
public WebTable[] getTables()<br />
public WebTable getTableStartingWith(String text)<br />
public WebTable getTableStartingWithPrefix(String text)<br />
public WebTable getTableWithID(String text)<br />
public WebTable getTableWithSummary(String text)<br />
Es gibt noch weitere Methoden, um speziellen Content zu extrahieren. Dies kann nicht<br />
nur mit Hilfe <strong>der</strong> von HttpUnit bereit gestellten Methoden erfolgen, son<strong>der</strong>n <strong>der</strong><br />
9
Kapitel 3: Testen von Webanwendungen<br />
Programmierer selbst kann mit Hilfe <strong>der</strong> Methode getDOM() durch die Knoten einer<br />
Seite navigieren und sich spezielle Instanzen rückliefern lassen. Ermöglicht wird dieses<br />
dadurch, dass die Syntax von Webseiten dem Document Object Model (DOM) des<br />
World Wide Web Consortium unterliegt. Die bereitgestellten Methoden <strong>der</strong> Klasse<br />
WebResponse nutzen ebenfalls diese Struktur, um den Content zu extrahieren. Die<br />
Struktur des zurück gelieferten Ergebnisses ist somit wie ein Baum aufgebaut, dessen<br />
Knoten von <strong>der</strong> Wurzel an durchsucht werden können. Dies ermöglicht, bestimmte<br />
Werte (zum Beispiel alle als deklarierten Überschriften) in eine Tabelle o<strong>der</strong><br />
einen Vektor zu schreiben und somit vom Rest des Contents zu separieren.<br />
HttpUnit gibt nicht nur die Möglichkeit, Zugriff auf den Content einer Webseite zu<br />
erhalten, son<strong>der</strong>n kann auch mit Cookies arbeiten. Hier<strong>für</strong> stehen zwei Methoden <strong>der</strong><br />
Klasse WebResponse bereit, zum einen getNewCookieNames() und<br />
getNewCookieValue(). Hierdurch kann überprüft werden, ob <strong>der</strong> Server versucht,<br />
ein Cookie zu setzen und ob die Werte, die das Cookie zu setzen versucht, die Richtigen<br />
sind.<br />
3.1.3 Auswertung <strong>der</strong> Ergebnisse<br />
Alleine das Separieren von Teilen <strong>der</strong> übermittelten Seite sagt natürlich noch nichts<br />
über die Korrektheit <strong>der</strong> Daten aus. Hierzu müssen die empfangenen dann mit den<br />
erwarteten Daten verglichen werden. Bedenkt man, dass jede einzelne Seite einer<br />
Webanwendung an<strong>der</strong>s ist, müsste man hier im Grunde <strong>für</strong> jede eine eigene Testklasse<br />
schreiben. Der Aufwand hier<strong>für</strong> ist allerdings größer als <strong>der</strong> daraus erzielte Nutzen. Aus<br />
diesem Grund werden Testfälle nur <strong>für</strong> Seiten geschrieben, bei denen <strong>der</strong> manuelle<br />
Aufwand des Testens größer ist als <strong>der</strong> Aufwand des Schreibens eines Testfalls <strong>für</strong> die<br />
Anwendung. Ein denkbarer Fall hier<strong>für</strong> ist zum Beispiel ein Formular mit mehreren<br />
Eingabemöglichkeiten und daraus resultierenden verschiedenen Ergebnissen. Hier<br />
durchläuft das Testprogramm dann eine Tabelle mit den möglichen Eingaben und<br />
vergleicht die zurück gelieferten Ergebnisse mit den ebenfalls in <strong>der</strong> Tabelle<br />
gespeicherten richtigen Ergebnissen. Auch wenn dies bei einem einmaligen Test den<br />
gleichen Aufwand wie bei einem manuellen Test aufweist, so reduziert sich <strong>der</strong><br />
Aufwand schon, wenn man ein zweites Mal testet, nachdem sich zum Beispiel etwas an<br />
<strong>der</strong> Seite geän<strong>der</strong>t hat. Da <strong>der</strong> Test selber nicht geän<strong>der</strong>t werden muss, kann er erneut<br />
wie<strong>der</strong> eingesetzt werden.<br />
10
Kapitel 3: Testen von Webanwendungen<br />
Ein Beispiel hier<strong>für</strong> ist das oben genannte Login-Formular. Erscheint nach dem Login<br />
eine Seite mit dem Titel „Login erfolgt“, kann durch einfaches Testen geprüft werden,<br />
ob <strong>der</strong> Login erfolgreich war:<br />
WebConversation conv = new Webconversation();<br />
WebResponse response = conv.getResponse(“http://www.wi.unimuenster.de/”);<br />
WebForm form = response.getForms()[0];<br />
WebRequest loginreq = form.getRequest();<br />
loginreq.setParameter (“username”, user);<br />
loginreq.setParameter (“password”, pass);<br />
WebResponse loginresponse = conv.getResponse(loginreq)<br />
assertEquals (“Login erfolgt”, loginresponse.getTitle());<br />
Hier wurde nun einfach geprüft, ob <strong>der</strong> Titel <strong>der</strong> empfangenen Seite mit dem erwarten<br />
übereinstimmt. Sollte dies <strong>der</strong> Fall sein, erscheint im JUnit TestRunner ein grüner<br />
Balken und <strong>der</strong> Test wurde erfolgreich ausgeführt. Schlägt <strong>der</strong> Vergleich fehl, so wird<br />
<strong>der</strong> Balken im TestRunner rot und <strong>der</strong> Fehler wird ausgeliefert.<br />
Ein weiterer möglicher Einsatz ist das Schreiben eines Tests, welcher Gegebenheiten<br />
testet, die auf verschiedene Seiten zutreffen. So ist laut [HOVPG04] das Testen aller<br />
Links auf einer Seite und aller weiterführenden Links mit einer einfachen Testklasse<br />
möglich:<br />
private WebConversation conversation;<br />
private Set checkedlinks;<br />
private String host = www.uni-muenster.de;<br />
public void setUp(){<br />
conversation = new WebConversation();<br />
checkedLinks = new HashSet();<br />
}<br />
public void testEntireSite() throws Exception{<br />
WebResponde response =<br />
conversation.getResponse(“http://”+host);<br />
checkAllLinks(response);<br />
System.out.prinln(“Alle Links überprüft”);<br />
}<br />
private void checkAllLinks (WebResponse response) throws<br />
Exception{<br />
if(!isHtml(response))return;<br />
WebLink[] links = response.getLinks();<br />
for(int i = 0; i < links.length; i++){<br />
boolean newLink = checkedLinks.add(links[i].getURLString());<br />
if(newLink){<br />
checkLink(links[i]);<br />
}<br />
}<br />
}<br />
11
Kapitel 3: Testen von Webanwendungen<br />
private boolean isHtml(WebResponse response){<br />
return response.getContentType().equals(“text/html);<br />
}<br />
private void checkLink(WebLink link) trhows Exception{<br />
WebRequest request = link.getRequest();<br />
java.net.URL url = request.getURL();<br />
String linkHost = url.getHost();<br />
if(linkHost.equals(this.host)){<br />
WebResponse response = conversation.getResponse(request);<br />
this.checkAllLinks(response);<br />
}<br />
}<br />
Zuerst wird hier wie<strong>der</strong> mit setUp() eine Verbindung mit einem Server aufgebaut und<br />
ein Hashset <strong>für</strong> die zu überprüfenden Links bereitgestellt. Mit testEntireSite()<br />
wird nun eine Anfrage an <strong>der</strong> Server gestellt und danach checkAllLinks()<br />
aufgerufen. Die Methode checkAllLinks() ruft zuerst isHtml() auf, um zu<br />
überprüfen, ob es sich auch um eine (auswertbare) Html-Seite handelt. Sollte dies <strong>der</strong><br />
Fall sein, werden alle in <strong>der</strong> Seite enthaltenen Links in ein Array geschrieben. Dieses<br />
Array wird nun durchwan<strong>der</strong>t und <strong>für</strong> jeden Eintrag die Methode checkLink()<br />
aufgerufen. Hier wird nun überprüft, ob es sich um einen internen o<strong>der</strong> externen Link<br />
handelt. Externe werden nicht überprüft, da sie nicht zum eigenen Projekt gehören.<br />
Handelt es sich um einen internen Link, wird <strong>für</strong> diesen Link ein Request durchgeführt.<br />
Wird dabei ein ungültiges Ergebnis zurückgeliefert, so handelt es sich um einen<br />
defekten Link und die Exception wird ausgelöst, die wie<strong>der</strong> im JUnit TestRunner<br />
ausgegeben wird. Da rekursiv alle Links <strong>der</strong> Webseite überprüft werden, können so alle<br />
fehlerhaften Links entdeckt werden. Um ein an<strong>der</strong>es Projekt zu testen, muss lediglich<br />
<strong>der</strong> Host geän<strong>der</strong>t werden. Dies ist nur ein kleiner Testfall, <strong>der</strong> sich lediglich auf<br />
statische Inhalte anwenden lässt, da dynamische Seiten und Formulare ignoriert werden.<br />
3.2 Testen mit Jakarta Cactus<br />
Im Gegensatz zu HttpUnit, bei dem funktionale Tests von Webseiten durchgeführt<br />
wurden, werden bei Jakarta Cactus Integrations-Tests durchgeführt. Cactus stellt hierzu<br />
drei Unterklassen bereit, um serverseitigen Java-Code zu testen. Hiermit ist es möglich,<br />
Servlets [HOVPG04, S. S. 361], JSPs [HOVPG04, S. 371] und Servlet Filter<br />
[HOVPG04, S.365] zu testen. Weiterhin ermöglicht es, EJBs [HOVPG04, S. 394] mit<br />
Cactus zu testen. Da dies aber keine spezielle Technik nur <strong>für</strong> Webanwendungen<br />
darstellt, wird dies in dieser Arbeit nicht betrachtet.<br />
12
Kapitel 3: Testen von Webanwendungen<br />
3.2.1 Generelle Funktionsweise von Jakarta Cactus<br />
Die Beson<strong>der</strong>heit bei Cactus ist, dass zwei Testfälle erzeugt werden, zum einen auf <strong>der</strong><br />
Clientseite, zum an<strong>der</strong>en auf <strong>der</strong> Serverseite.<br />
Abbildung 5: Ausführung eines Cactus-Tests [Ja07]<br />
Der JUnit Test Runner führt den Test zunächst auf <strong>der</strong> Clientseite aus. Hierzu wird die<br />
Methode runTest() ausgeführt. Für jede testXXX() Methode wird nun nach einem<br />
beginXXX() gesucht. In beginXXX() können auch weitere Parameter gesetzt werden,<br />
welche zur Ausführung des serverseitigen Codes notwendig sind. (1)<br />
Anschließend wird eine Verbindung mit einem Redirector aufgebaut und <strong>der</strong> Request,<br />
mit allen ihm übergebenen Parametern, wird an ihn gesendet. (2)<br />
Der Redirector fungiert auf Serverseite wie <strong>der</strong> JUnit Test Runner auf <strong>der</strong> Client Seite.<br />
Hierzu wird <strong>der</strong> Redirector initialisiert und bekommt alle Parameter <strong>für</strong> die<br />
Testausführung übergeben. (3)<br />
Nach <strong>der</strong> Initialisierung führt <strong>der</strong> Redirector nacheinan<strong>der</strong> die Methoden setUp(),<br />
testXXX() und tearDown() aus. (4)<br />
Bei <strong>der</strong> Ausführung <strong>der</strong> testXXX()-Methode werden <strong>der</strong> serverseitige Java-Code (JSP,<br />
Servlet o<strong>der</strong> Servlet Filter) ausgeführt und JUnit Methoden zum Auswerten <strong>der</strong><br />
Ergebnisse benutzt. (5)<br />
Die Ergebnisse des Tests (egal ob erfolgreich o<strong>der</strong> misslungen) werden vom Redirector<br />
eingesammelt. (6)<br />
Sind alle Tests ausgeführt, schickt <strong>der</strong> Redirector die Ergebnisse zurück zum Client. (7)<br />
13
Kapitel 3: Testen von Webanwendungen<br />
Wurden Fehler bei <strong>der</strong> Ausführung <strong>der</strong> Tests gefunden, so werden diese nun im JUnit<br />
Test Runner angezeigt. Wurden keine Fehler gefunden, sucht runTest() nun nach<br />
einem endXXX() und führt es aus. Hierbei ist es erneut möglich, die empfangenen<br />
Daten mit JUnit zu überprüfen.<br />
3.2.2 Testen von Servlets<br />
Servlets stellen Java-Klassen dar, die Http-Anfragen entgegennehmen und diese<br />
beantworten. Dabei kann je nach Parameter <strong>der</strong> Inhalt dynamisch erzeugt werden.<br />
Jakarta Cactus stellt hier<strong>für</strong> eine junit.framework.TestCase Unterklasse bereit:<br />
org.apache.cactus.SevletTestCase<br />
Um einen Testfall zu generieren, muss <strong>der</strong> Programmier drei Methoden erzeugen<br />
(beginXXX(), testXXX() und endXXX()). Außerdem muss in <strong>der</strong> web.xml (einer<br />
Datei, die bei <strong>der</strong> Installation von Cactus auf den Server gelegt wird) ein Eintrag<br />
vorgenommen werden, damit <strong>der</strong> Testfall den entsprechenden Redirector <strong>für</strong> Servlets<br />
benutzt [HOVPG04, S. 361]:<br />
<br />
ServletRedirector<br />
<br />
org.apache.cactus.server.ServletTestRedirector<br />
<br />
<br />
<br />
ServletRedirector<br />
/ServletRedirector<br />
<br />
Mit <strong>der</strong> Methode beginXXX() wird eine Verbindung mit dem Server hergestellt. Ihm<br />
werden zum einen die URL <strong>der</strong> zu testenden Seite übergeben als auch notwendige<br />
Parameter zur Initialisierung. Des Weiteren können zum Beispiel noch Cookies <strong>für</strong> den<br />
Request gesetzt werden [Ja07]:<br />
public void beginXXX(WebRequest theRequest){<br />
theRequest.setURL("uni-muenster.de", "/app", "/test.jsp", null,<br />
null);<br />
theRequest.addCookie("cookiename", "cookievalue");<br />
}<br />
Cactus erstellt automatisch ein WebRequest Objekt und stellt es unter den Namen<br />
„response“ den Methoden setup(), testXXX() und teardown() bereit. Dieses<br />
Objekt verweist auf das in beginXXX() erstellte WebRequest Objekt. In <strong>der</strong><br />
14
Kapitel 3: Testen von Webanwendungen<br />
testXXX()-Methode wird nun das eigentliche Servlet getestet. Mit <strong>der</strong> Funktion<br />
assertEquals() kann zum Beispiel <strong>der</strong> Inhalt von Attributen auf Übereinstimmung<br />
überprüft werden [Ja07]:<br />
public void testXXX(){<br />
MyServletToTest servlet = new MyServletToTest();<br />
servlet.init(config);<br />
servlet.methodToTest();<br />
assertEquals("someValue",<br />
session.getAttribute("someAttribute"));<br />
assertEquals("uni-muenster.de", request.getServerName());<br />
}<br />
Das zurück gelieferte Ergebnis kann nun noch in <strong>der</strong> endXXX()-Methode ausgewertet<br />
werden. Hier kann wie<strong>der</strong>um ähnlich wie bei HttpUnit zum einen auf den Content des<br />
Ergebnisses als auch auf MetaDaten und Cookies zugegriffen werden [Ja07]:<br />
public void endXXX(WebResponse theResponse){<br />
Cookie cookie = theResponse.getCookie("someCookie");<br />
assertEquals("someValue2", cookie.getValue());<br />
assertEquals("some content here", theResponse.getText());<br />
}<br />
3.2.3 Testen von Servlet-Filtern<br />
Servlet-Filter dienen dazu, vor und/o<strong>der</strong> nach dem Zugriff auf ein Servlet Informationen<br />
dem Servlet hinzuzufügen o<strong>der</strong> Informationen herauszufiltern und diese zum Beispiel<br />
auf dem Server abzulegen. Um diese Filter zu testen, muss wie<strong>der</strong> eine spezielle<br />
Testklasse eingebunden werden:<br />
org.apache.cactus.FilterTestCase<br />
Auch hier muss dem Programm mittels Eintrag in <strong>der</strong> web.xml mitgeteilt werden,<br />
welcher Redirector verwendet wird:<br />
<br />
FilterRedirector<br />
<br />
org.apache.cactus.server.FilterTestRedirector<br />
<br />
<br />
<br />
FilterRedirector<br />
/FilterRedirector<br />
<br />
Jetzt kann <strong>der</strong> eigentliche Test geschrieben werden. Die beginXXX()-Methode<br />
funktioniert wie oben beschrieben und beschreibt lediglich den Request an eine Seite.<br />
15
Kapitel 3: Testen von Webanwendungen<br />
Mit <strong>der</strong> testXXX()-Methode kann nun ein Filter auf einen Response ausgeführt<br />
werden. Im folgenden Beispiel wird ein Hea<strong>der</strong> und ein Footer dem Response zugefügt<br />
[Ja07]:<br />
public void testXXX() throws ServletException, IOException{<br />
SampleFilter filter = new SampleFilter();<br />
config.setInitParameter("hea<strong>der</strong>", "Kopf");<br />
config.setInitParameter("footer", "Fuss");<br />
filter.init(config);<br />
FilterChain mockFilterChain = new FilterChain() {<br />
public void doFilter(ServletRequest theRequest,<br />
ServletResponse theResponse) throws IOException,<br />
ServletException{<br />
PrintWriter writer = theResponse.getWriter();<br />
writer.print("Inhalt");<br />
writer.close();<br />
}<br />
public void init(FilterConfig theConfig){<br />
}<br />
public void destroy(){<br />
}<br />
};<br />
filter.doFilter(request, response, mockFilterChain);<br />
}<br />
Hier wird nun zuerst <strong>der</strong> Filter initialisiert, die Parameter <strong>für</strong> den Footer und Hea<strong>der</strong><br />
gesetzt und dann <strong>der</strong> Filter ausgeführt. Um zu testen, ob das zurück gelieferte Ergebnis<br />
auch wirklich den Footer sowie den Hea<strong>der</strong> enthält, kann wie<strong>der</strong> die endXXX()-<br />
Methode benutzt werden [Ja07]:<br />
public void endXXX(WebResponse theResponse){<br />
assertEquals("hea<strong>der</strong>some<br />
contentfooter", theResponse.getText());<br />
}<br />
Abschließend wird lediglich noch geschaut, ob die Informationen, die <strong>der</strong> Filter dem<br />
Response hinzugefügt hat, auch vorhanden sind.<br />
3.2.4 Testen von JSPs<br />
JavaServer Pages (JSP) ist eine Technologie, die es erlaubt, Java-Code in statische<br />
Inhalte einzubetten. Hierdurch ist es möglich, dass Inhalt und Design getrennt<br />
voneinan<strong>der</strong> behandelt werden und mittels <strong>der</strong> JSPs dynamische Inhalte eingebunden<br />
werden. Eine Einbindung <strong>der</strong> entsprechenden Testklasse ist obligatorisch:<br />
org.apache.cactus.JspTestCase<br />
16
Kapitel 4: Zusammenfassung und Fazit<br />
Um hier wie<strong>der</strong> dem Testfall einen Redirector zuzuweisen, muss dieser auf bekannte<br />
Weise in die web.xml eingetragen werden.<br />
Das Testen von JSPs beschränkt sich jedoch auf den Test <strong>der</strong> zurück gelieferten Seiten<br />
(zum Beispiel HTML o<strong>der</strong> XML). Man stellt im Grunde lediglich eine Anfrage an diese<br />
Seite mit dem integrierten JSP und übergibt die benötigten Parameter:<br />
Danach kann wie<strong>der</strong> auf Client-Seite die Antwort mit endXXX() ausgewertet werden.<br />
Da das zurück gelieferte Ergebnis einer „normalen“ Internetseite entspricht, können hier<br />
wie<strong>der</strong> von JUnit bereit gestellte Methoden verwendet werden, um das Ergebnis<br />
auszuwerten.<br />
4 Zusammenfassung und Fazit<br />
Mit HttpUnit und Jakarta Cactus werden zwei unterschiedliche Erweiterungen <strong>für</strong> JUnit<br />
bereitgestellt. Während mit HttpUnit funktionale Tests von Webanwendungen<br />
durchgeführt werden, bietet Jakarta Cactus die Möglichkeit, Integrationstests <strong>für</strong><br />
serverseitige Java-Anwendungen durchzuführen.<br />
Durch immer komplexer werdende Webanwendungen ist hier, wie auch in den an<strong>der</strong>en<br />
Bereichen des Software Engineering, ein Testen des erstellten Codes <strong>für</strong> eine<br />
komplikationslose Entwicklung erfor<strong>der</strong>lich. Auf Grund <strong>der</strong> Simplizität <strong>der</strong><br />
bereitgestellten Methoden lassen sich schnell Testfälle entwickeln, um<br />
Webanwendungen während <strong>der</strong> Entwicklung auf einwandfreie Funktion zu überprüfen.<br />
Da die beiden Erweiterungen unterschiedliche Teile <strong>der</strong> Webanwendung testen und<br />
somit unterschiedliche Tests durchführen, gibt es keine Bevorzugung einer Erweiterung.<br />
Vielmehr braucht man <strong>für</strong> größere Projekte sowohl Jakarta Cactus als auch HttpUnit,<br />
um die Anwendungen komplett zu testen. Um die Tests noch unkomplizierter zu<br />
gestalten, bietet Jakarta bei seinem Cactus Projekt mittlerweile die Integration von<br />
HttpUnit an.<br />
Durch beide Erweiterungen besteht die Möglichkeit, manuelle Tests abzulösen und das<br />
Testen wesentlich effizienter zu gestalten. Die Zeitersparnis auf Grund des Testens lässt<br />
sich somit in an<strong>der</strong>e Bereiche <strong>der</strong> Entwicklung Nutzen bringend einsetzen und steigert<br />
hierdurch die Qualität des Endproduktes.<br />
17
Literaturverzeichnis<br />
[Fe04] Balazs Fejes, Test Web applications with HttpUnit,<br />
http://www.javaworld.com/javaworld/jw-04-2004/jw-0419httpunit.html?page=1,<br />
2004, Abrufdatum: 2007-04-18.<br />
[HOVPG04] Richard Hightower, Warner Onstine, Paul Visan, Damon Payne, Joseph<br />
D. Gradecki: Java Tools for extreme Programming, Wiley Publishing, 2004.<br />
[Ja07] The Jakarta Projekt: Jakarta Cactus, http://jakarta.apache.org/cactus/,<br />
Abrufdatum: 2007-04-20.<br />
[Ju07] JUnit.org: JUnit, Testing Ressources for Extreme Programming,<br />
http://www.junit.org/. Abrufdatum: 2007-05-10.<br />
[KWTRF06] Axel Kalenborn, Thomas Will, Rouven Thimm, Jana Raab, Ronny<br />
Fregin: Java-basiertes automatisiertes Test-Framework,<br />
<strong>Wirtschaftsinformatik</strong> 48 (2006), S.437-445.<br />
[Li05] Johannes Link: Softwaretests mit JUnit, dpunkt.verlag, 2005.