User-Interface Tests mit Selenium

17.09.2013
Martin Fleischer

Automatisierte Tests sowie deren Durchführung im Rahmen einer Continuous Integration stellt einen wesentlichen Faktor zur Sicherstellung einer hohen Qualität in Softwareprojekten dar. Die Entwicklung und Automation von Tests nimmt daher in den meisten unserer Softwareprojekten bzw. deren unserer Kunden eine wichtige Rolle ein. Seit geraumer Zeit erfahren besonders Tests mit Selenium - die das Verhalten der Software direkt auf der Benutzeroberfläche testen - große Beliebtheit.

Test Setup

In unseren aktuellen Projekten kommen überwiegend in Ruby implementierte Testsuites zum Einsatz. Hier wird vorrangig auf dem Behaviour-Driven Development Testtool RSpec aufgesetzt. Bei der Umsetzung der Seleniumtests haben wir uns ebenfalls an diesem Setup orientiert.

Desweiteren wurde Capybara in das Testsetup integriert. Neben einer gut lesbaren DSL für die Webdriver API stellt das Testframework vor allem Hilfsmethoden für asynchrones JavaScript und zum Verhindern von race conditions bereit (im Kontext von Webapplikation können diese leicht durch das unvorhersagbare Ladeverhalten referenzierter JavaScript Ressourcen auftreten).

Das Test-Setup wurde zudem um Funktionen und Pakete erweitert, die unter anderem die parallele Ausführung sowie das Aufzeichnen von Screenshots (und sogar ganzer Videocaptures) der Testdurchführung ermöglichen. Für letzteres wurde das Headless Gem ("Headless is the Ruby interface for Xvfb") verwendet - so ist eine Testausführung "headless" auch auf CI-Servern möglich. Tests “headless” auszuführen bedeutet, diese ohne das Vorhandensein einer grafischen Oberfläche - also zum Beispiel scriptgesteuert auf einem Server - zu starten.

Das Headless Gem verwendet Xvfb, um UI-Tests innerhalb einer Firefox Instanz auch ohne installierten Windowmanager auszuführen. Xvfb ist ein X-Server, der einen virtuellen Framebuffer nutzt, um ein angeschlossenes Display zu simulieren. Da die Frames hierbei im Arbeitsspeicher landen, kann auf diese zugegriffen werden, was das Aufzeichnen von Screenshots und Videos der Testausführung ermöglicht. Durch die Auswertung (manuell oder teilautomatisiert) der Screenshots und Videos können mögliche Fehlerquellen schnell identifiziert werden.

Testaufbau

Um Seleniumtests möglichst schnell und unkompliziert an Änderungen in der Oberfläche anpassen zu können, wurden die Interaktionselemente der Oberfläche der Anwendung in entsprechende Module ausgelagert. Zur Identifikation und Selektion von Elementen nutzt Selenium CSS-Selektoren (also z.B. class- oder id-Attribute) und über Xpath lassen sich komplexere Element(-gruppen) selektieren. Capybara stellt zudem einen Wrapper mit gut lesbaren Finder-Methoden bereit.

Die Bezeichnung von Element-Selektoren ist bei Änderungen an der Oberfläche in der Regel häufigen Änderungen unterworfen. Daher sollte dieser an einer zentralen Stelle definiert werden, um sicherzustellen, das nicht eine Vielzahl unterschiedlicher Code-Segmente angepasst werden müssen. In Anlehnung an das Page Object Patterns, gibt es für jede zu testende Seite Module, die diese Selektoren über statische Methoden bereitstellen.

Wiederkehrende Aktionen - bspw. das Login auf einer Seite - wurden in zentrale Helper ausgelagert ("ui-actions"), die in vielen Tests einfach wiederverwendet werden können.

Continuous Integration

Die Test-Suites sind so konfiguriert, dass diese im Rahmen einer CI direkt gegen Testumgebungen der Software ausführbar sind. Die Laufzeit eines Test (headless Start einer Firefox-Instanz) kann bei sequentieller Durchführung mittlerer bis größerer Test-Suiten zum Problem werden. Abhilfe schafft hier die Parallelisierung der Testausführung. Dies kann durch Einsatz des parallel_tests Gem erreicht werden - eine entsprechende Testumgebung vorausgesetzt. Auch die Anlage von Testdaten wurde über REST-Schnittstellen vereinfacht, so dass z.B. Nutzer direkt über eine Schnittstelle angelegt werden können, ohne jedes Mal die Anlage eines Nutzer automatisiert über die Oberfläche durchzuführen. Die Anlage von Nutzer über die Oberfläche wird nur in einem einzigen separaten Test überprüft - eine wiederholte Durchführung ist also überflüssig.

Kritik

Die Community steht dem Einsatz automatisierter UI-Test nicht ganz ohne Kritik gegenüber (Beispiel). Die Hauptargument dabei sind:

Oberflächen sind visuelle Benutzerschnittstellen, die auf Usability optimiert werden, was schwer zu prüfen ist.

Außerdem stellt es eine Herausforderung dar, von den Fehlermeldungen der UI-Tests auf den Fehler oder einen eventuellen Bug zu schließen. Eine manuelle Auswertung der Screen- bzw. Videocaptures, wie zuvor beschrieben, ist hier besonders hilfreich.

Häufig muss eine hohe Entwicklungszeit in den sauberen Aufbau der Suite und ihrer Tests investiert werden. Die Wartung der Test-Suite ist aufgrund der Änderungen der Benutzeroberfläche und der somit erforderlichen Anpassungen ebenfalls recht aufwändig. Das Vorhandensein einer Test-Suite ist jedoch ein enormer Gewinn bei der Sicherstellung der Qualität einer Anwendung. Zudem lässt dich der Wartungsaufwand durch die oben beschrieben Maßnahmen (bspw. Modularisierung) entsprechend minimieren.

Das Ausführen einer größeren UI-Testsuite, das Laden und Rendern von Webseiten, sowie die Interaktion mit Elementen, die teils über JavaSrcipt nachgeladen werden, braucht Zeit. Im Rahmen einer CI - und des gewünschten schnellen Feedbacks - ist selbst eine parallele Ausführung aller UI Tests noch zu langsam. Hier kann durch eine partielle oder gesonderte Ausführung der UI-Tests Abhilfe geschaffen werden.

UI-Tests sind anfällig nicht-determinstisch zu sein, da es nach Änderungen in der Oberfläche oder auch bei Ladezeit-bedingten Problemen zu Fehlschlägen kommen kann. Dies kann sich zu einem großen Problem entwickeln, da der Test an sich nutzlos ist und die Glaubwürdigkeit der Tests mit allzu häufigen Fehlschlägen stark nachlässt (ein guter Artikel zum non-determinism Problem findet sich in Martin Fowlers Blog). Im Ergebnis muss daher viel Wert auf die Robustheit der Tests gelegt werden.

Fazit

Das Schreiben von Selenium Tests macht Spaß und stellt das korrekte Verhalten der Nutzeroberfläche sicher. Sie taugen für Abnahmetest aus Benutzersicht und man kann mit ihnen hervorragend Nutzerworkflows abbilden. Es ist zweifellos richtig, das UI-Tests kein Allheilmittel darstellen, sie sind jedoch eine sinnvolle zusätzliche Maßnahme zur Steigerung und Sicherstellung der Qualiät und sollten dazu genutzt werden, wofür sie gedacht sind: Zum Testen der Oberfläche.

Eine ausgewogene Testsuite sollte auf vielen Low-Level Tests aufbauen. Die UI-Tests nehmen nur einen kleinen Teil der Testsuite ein. Mike Cohn stellte in seinem Blog das Konzept der Test Automation Pyramid mit den UI-Tests als Spitze einer Pyramide vor, die einen solchen Aufbau empfiehlt.

test pyramide

Weitere Artikel zum Thema liefern z.B. Martin Fowlers zur Test Pyramide. Seiner Meinung nach ist die Aussage der Pyramide im Kern richtig, reicht jedoch nicht aus, um die Komplexität einer gut ausbalancierten Testsuite zu beschreiben. Hervorzuheben ist in seinem Artikel die treffende Bemerkung, dass ein fehlgeschlagener Test nicht nur auf einen Bug, sondern auch auf das Fehlen eines Unit-Tests hinweist. Das ice-cream-cone Anti-Pattern von Alister Scott liefert eine nette Anekdote zur Pyramide.

Martin Fleischer
comments powered by Disqus