Sonntag, 12. Februar 2012


Artikel

Februar 2010 | Artikel

"Klick" ist kein Testpattern!

(Link zum Artikel: http://www.entwickler-magazin.de/jaxenter//002887)

Die FacesTales-Kolumne

Text: Lars Röwekamp und Matthias Weßendorf
  • Teilen
  • kommentieren
  • empfehlen
  • Bookmark and Share
Das Testen von Webanwendungen lässt sich nicht unbedingt als "Quelle der Freude" bezeichnen. Dies gilt leider auch für JSF-Anwendungen. Mittlerweile existieren einige, auf die speziellen Belange von JSF zugeschnittene Testframeworks, die Erleichterung bringen sollen. Fragt man einen Entwickler einer mehrschichtigen Webanwendung, ob diese denn auch automatisch getestet werden, dann ist die Antwort in der Regel ein stolzes "Ja". Fragt man im Anschluss, ob die Tests denn auch die View-Elemente mit einbeziehen, erntet man im Allgemeinen ungläubiges Staunen. Alternativ erhält man die Antwort, dass dies viel zu kompliziert sei und die Tests daher lieber manuell – also via Klick – durchgeführt werden. Warum ist das so?

Die meisten Webtestwerkzeuge basieren auf dem Blackboxprinzip. Ein HTTP-Request wird an einen emulierten Browser geschickt, der die Zielumgebung für die deployte Anwendung darstellt. Dann erfolgt ein Vergleich des zurückgesendeten HTML-Codes mit erwarteten Fragmenten. Leider hat dieses Verfahren für JSF-Anwendungen ein paar entscheidende Nachteile. Erstens wird nicht das Resultat eines realen Browsers getestet, zweitens sagt der Test nichts über die Korrektheit des im Hintergrund aufgebauten JSF-Komponentenbaums aus. HTML-Ergebnis und serverseitiger Zustand sind nur schwer in Einklang zu bringen. Ein weiterer Nachteil ist häufig das Fehlen einer ausgereiften Ajax-Testunterstützung, da vom klassischen Request-Response-Modell ausgegangen wird. Für Probleme sorgen auch internationalisierte oder mandantenfähige Webanwendungen mit unterschiedlichen Look-and-Feels.

Eine Alternative stellt das Whitebox Testing dar. Hier wird versucht, das Laufzeitverhalten des Servers durch Fake-Objekte (Mocks) zu simulieren. Da die zu testende Anwendung dabei nicht wirklich laufen muss, können Whitebox-Tests einfacher in JUnit- oder TestNG-Tests verwendet werden. Nachteil: Soll die gesamte Anwendung getestet werden, ist das Setup für die Mocks häufig genauso umfangreich wie die Anwendung selbst. Die alternative Verwendung generischer Mockup Libraries dagegen verlangt sehr gute Kenntnisse der JSF-Interna. Ist der Einwand vieler JSF-Entwickler somit begründet, dass Web-Testing zu umständlich ist und kaum Vorteile mit sich bringt? Ein Blick auf die beiden Frameworks Selenium und JSFUnit soll diese Frage beantworten.

Selenium

Selenium ist ein JavaScript-basiertes Open-Source-Web-Testing-Tool, das das Testen von Web-2.0-Anwendungen in verschiedensten, realen Browsern auf unterschiedlichen Betriebssystemen erlaubt. Um das zu realisieren, ermöglicht Selenium das Lokalisieren von HTML- bzw. DOM-Elementen via ID, Name, Dom, xPath-Ausdrücken oder CSS-Selektoren, das Manipulieren des States einer Anwendung durch die Ausführung von Actions inklusive "warten" auf asynchrone Results, das Speichern von Werten in lokalen Variablen zwecks späterer Wiederverwendung sowie das Vergleichen des States der Anwendung mit erwarteten Werten. Selenium-Tests können auf verschiedene Arten durchgeführt werden. Am einfachsten ist die Definition in einer HTML-Datei über eine dreispaltige Tabelle (Kommando, Parametername, Parameterwert) und das anschließende Ausführen des Tests durch den Test-Runner – einer im Paket enthaltenen HTML-basierten Anwendung. Nachteile dieses Verfahrens sind die durch das HTML-Format eingeschränkten Möglichkeiten sowie die Codeduplikation bei ähnlichen Tests. Eine Alternative stellt der Record-Playback-Mode (auch als Selenium DIE bekannt) dar. Mit dem Firefox Plug-in können Testszenarien innerhalb des Browsers aufgezeichnet und beliebig oft abgespielt – also getestet – werden. Der Record-Playback-Mode ist somit als Automatisierung des klassischen "Durchklickens" zu verstehen. Zwar sind in diesem Modus nur sehr beschränkte Tests möglich, aufgezeichnet können aber u. a. nach HTML (als Basis für den Test-Runner) oder Java exportiert werden. Der Record-Playback-Mode eignet sich somit gut zum initialen Erzeugen von Testgerüsten.

Der wohl wichtigste Modus von Selenium ist der Remote-Control-Modus. Mit seiner Hilfe können aufgezeichnete oder selbst geschriebene Tests in verschiedene Sprachen – natürlich auch Java – genutzt werden. In der Regel macht es Sinn, den Test in Kombination mit einem Testframework (JUnit, TestNG) zu verwenden. Selenium verbindet sich im Remote-Control-Modus mit einem auf Java-Script-basierenden Selenium-Server, der wiederum den gewünschten realen Browser startet. Das Selenium-RC-API bietet für Java-Entwickler umfangreiche Möglichkeiten zur Implementierung von Tests. Angefangen beim Auslösen einfacher Events (click, keyPressed, fireEvent) über das Simulieren von Browsernavigation (GOBACK, REFRESH) bis hin zu einer Vielzahl von Methoden zur Evaluierung des Ergebnisses. Die oben beschriebene Ajax-Unterstützung gibt es auch in diesem Modus, wobei Selenium hierfür eigene Methoden bereitstellt, die Veränderungen am DOM überwachen können.

Selenium-Tests lassen sich relativ einfach in den Build-Prozess und in die Continous-Integration-Umgebung einbinden. Die durch Tests erzeugten HTML-Reports können direkt an das Team „verteilt“ werden. Für Cross-Platform-Tests lassen sich diese auf mehrere Server und unterschiedliche Plattformen verteilen. Auch das parallele Ausführen der Tests auf mehreren Servern – zwecks Skalierung – ist möglich.

JSFUnit

Das Testing Framework JSFUnit aus dem Umfeld der JBoss-Community hat sich zum Ziel gesetzt, DAS Test-Framework für JSF-basierte Web-2.0-Anwendungen zu werden. In Anlehnung an das oben beschriebene Whitebox- bzw. Blackbox-Testing, bezeichnet sich JSFUnit selbst gerne als AcrylBox- bzw. GreyBox-Test-Framework. Durch diesen Kunstbegriff soll verdeutlicht werden, dass sowohl Unit- als auch Integration-Tests möglich sind. Um das zu realisieren, bieten die JSFUnit-APIs vollen Zugriff auf alle relevanten JSF-Bestandteile einer Webanwendung und auf den geparsten HTML-Output.

JSFUnit unterscheidet zwischen statischen Analysen, Performancetests und normalen Tests. Die statischen Analysen – die zurzeit leider nicht den GA-Status besitzen – ermöglichen das Parsen und Testen der JSF-spezifischen Konfigurationen und Klassen noch während des Build-Prozesses. Neben dem Testen von korrekten Managed-Bean-Referenzen sowie der Typenprüfung von JSF Action und Action-Listener-Referenzen innerhalb von EL-Ausdrücken, können auch die gesamte JSF-Konfiguration geprüft, oder aber eine Menge anderer zur Compile-Zeit feststellbarer Ungereimtheiten (Scope Mismatch, Duplicated Declarations, Missing Methods, Invalid Inheritance etc.) aufgedeckt werden.

Perfomanceanalysen werden in JSFUnit durch ein einfaches Konstrukt namens JSF Timer ermöglicht. Mithilfe des Timers können die Zeiten innerhalb der einzelnen JSF-Lifecycle-Phasen dediziert betrachtet und gestestet werden. So ist es z. B. möglich, einen Test fehlschlagen zu lassen, wenn ein gewisser Schwellwert in einer der Phasen überschritten wird. Im Rahmen der "normalen" Tests werden reale HTTP-Request gegen die laufende Anwendung geschickt und im Anschluss HTTP-Response und JSF-Artefakte untersucht. Die API erlaubt u. a. das Testen von Managed Beans inklusive Scoping, JSF-Navigation, View Components und Component Tree sowie Validation Rules durch Simulation ungültiger Eingaben.

Für die Durchführung sind zwei Klassen entscheidend: Die Klasse JSF-Client-Session dient als Wrapper für HtmlUnit und simuliert den Browser, und somit auch die Nutzereingaben während des Tests. Das funktioniert sowohl für normale Request-Response-Szenarien als auch – in der Regel – für Ajax-Calls. Die Klasse JFS-Server-Session liefert im Anschluss den Zugriff auf alle serverseitigen Objekte und somit den State der JSF-Anwendung. Im Falle der Verwendung von Rich-Faces-Ajax-Komponenten kann zusätzlich die Klasse Rich Faces Client zur optimierten Testunterstützung verwendet werden. Die Einbindung in den Build-Prozess sowie in die Continous-Integration-Umgebung wird durch spezielle Maven-Plug-ins sowie ANT-Tasks unterstützt.

Fazit

Das Testen von Webanwendungen im Allgemeinen und JSF-Anwendungen im Speziellen ist nach wie vor keine triviale Angelegenheit. Die Ausrede, dass ein Testen von JSF-Anwendungen mehr Aufwand verursacht als Nutzen bringt, kann man allerdings spätestens seit Selenium und JSFUnit nicht mehr gelten lassen. Selenium eignet sich gut für so genannte Smoke-Tests, d. h. zur Absicherung, dass die Anwendung im Großen und Ganzen so funktioniert wie erwartet. Ein Vorteil ist in der guten API sowie in der Unterstützung asynchroner Requests zu sehen. JSFUnit geht einen ganzen Schritt weiter und bietet umfangreiche Möglichkeiten zum Blackbox- und Whitebox-Testing an. Darüber hinaus können statische Analysen und Performancetests weitere Aufschlüsse über Probleme in der eigenen JSF-Anwendung liefern. Unabhängig von den in diesem Artikel gezeigten Tools sollte sich jeder JSF-Entwickler selbst einen großen Gefallen tun und bereits bei dem Design der JSF-Anwendung das Testen im Hinterkopf behalten. UseCases sollten von Beginn an mit entsprechenden TestCases versehen, und Abhängigkeiten zwischen JSF-Klassen minimiert werden, um so die Testbarkeit der Anwendung zu vereinfachen. "Tip-of-the-Day": IDs nicht vergessen!

Lars Röwekamp ist Geschäftsführer der OpenKnowledge GmbH und berät seit mehr als zehn Jahren Kunden in internationalen Projekten rund um das Thema Enterprise Computing.

Matthias Weßendorf arbeitet für die Oracle Corp. an ADF Faces und Apache Trinidad. Seit 2004 ist er PMC von Apache MyFaces. Matthias Weßendorf hat an verschiedenen internationalen Konferenzen als Redner teilgenommen.

  1. http://seleniumhq.org/
  2. http://www.jboss.org/jsfunit

andere Artikel dieser Serie

Kommentare