Stabile und wartbare Testfälle für Selenium

9. Januar 2014Arne-Michael Törsel

Automatisierte Testfälle, genauer gesagt zur automatischen Durchführung implementierte Testfälle, spielen in modernen Softwareentwicklungsprozessen eine wichtige Rolle. Als Regressionstest ermöglichen sie die kontinuierliche Integration, weil beschädigte Funktionalität schnell, regelmäßig und ohne manuellen Aufwand aufgedeckt werden kann. Neben der Automatisierung von Komponententests mit Unittest-Frameworks wie JUnit  können auch Systemtests, also Tests des vollständigen Anwendungssystems,  automatisiert werden. Solche Tests interagieren in der Regel direkt mit der Nutzeroberfläche der Anwendung.

Für den Test von Webanwendungen ist das Werkzeug Selenium sehr populär. Dies hat mehrere Gründe:

  • Mit der Webdriver API  bietet Selenium eine objektorientierte Schnittstelle zur Kommunikation mit einem Browser für viele Programmiersprachen (Java, Python, C#,  Ruby) und kann so einfach zusammen mit Testwerkzeugen wie JUnit, Robot oder FitNesse genutzt werden.
  • Die weitverbreitetsten Desktopbrowser Firefox, Internet Explorer, Chrome und Safari werden offiziell unterstützt.
  • Mit Selenium Grid kann ein Testlauf auf mehrere Testclients im Netzwerk verteilt werden. Somit können sehr große Testfallmengen schnell verarbeitet werden oder es kann parallel auf verschiedenen Browsern getestet werden.

In diesem Artikel möchte ich Ihnen einige Hinweise geben, wie Sie Ihre Selenium-Testfälle einerseits stabiler und andererseits wartbarer gestalten können. Ein Testfall ist dann stabil, wenn nicht jede (kleine) Änderung an der Weboberfläche auch eine Anpassung des Testfalls erfordert. Auch gegenüber Änderungen an anderen Testfällen sollte ein Testfall „immun“ sein. Sollten doch mal Anpassungen an Testfällen notwendig sein, ist es vorteilhaft, wenn diese einfach wartbar sind, weil nur an einer bzw. wenigen Stellen im Test-Quellcode Anpassungen erfolgen müssen und diese Stellen auch schnell und vollständig identifiziert werden können.

Testfälle organisieren

Testfälle hängen oft von einem existierenden Anwendungszustand ab und verändern diesen, zum Beispiel, indem in einer Webanwendung ein Nutzerkonto angelegt wird. Auf den ersten Blick ist es deshalb logisch, die Testfälle einfach so zu implementieren, dass ein erfolgreich ausgeführter Testfall die Vorbedingungen für den jeweils nächsten schafft. So könnte zum Beispiel ein Testfall zunächst ein Nutzerkonto anlegen und der folgende prüft dann, ob ein Login mit diesem Nutzerkonto möglich ist.

Dieses Vorgehen hat allerdings mehrere Nachteile. Erstens können auch alle nachfolgenden Testfälle eines fehlgeschlagenen Testfalls nicht mehr ausgeführt werden, bis die Ursache des Fehlschlags behoben ist. Vorhandene Fehler in der Anwendung bleiben dann unter Umständen lange unentdeckt, obwohl ein eigentlich passender Testfall existiert. Zweitens führt diese Organisation der Testfälle in der Regel dazu, dass sie eng miteinander gekoppelt sind und im Wartungsfall sehr viele nachgelagerte Testfälle angepasst werden müssen. Drittens ist es so nicht mehr möglich, Testfälle zur Geschwindigkeitssteigerung parallel auszuführen. Die zunächst aufwändigere Lösung ist es, für jeden Testfall separate Testdaten bzw. Ausgangszustände bereitzustellen. Dies ist nicht immer möglich, auf lange Sicht macht sich dieser Aufwand jedoch bezahlt.

Angesichts des Aufwands für die Testfallumsetzung ist Augenmaß bei der Auswahl der zu automatisierenden Testfälle notwendig. Setzen Sie zunächst Testfälle für die wichtigsten Funktionalitäten um. Gehen Sie dann lieber in die „Breite“ (grundlegende Abdeckung von weiteren Funktionalitäten) als in die „Tiefe“ (Abdeckung aller Randfälle einer Funktionalität). Konzentrieren Sie sich dazu auf Positivtestfälle. In der Regel ist es zum Beispiel nicht sinnvoll, Tests für die korrekte Behandlung von Fehleingaben zu automatisieren.

Testfälle strukturieren mit dem Page Object Pattern

Sind die umzusetzenden Testfälle ausgewählt, geht es darum, diese wartbar und stabil umzusetzen. Dazu kann das Page Object Pattern[1] eingesetzt werden. Kern dieses Entwurfsmusters ist die Kapselung der Interaktion mit der Webanwendung in separaten Objekten außerhalb der Testfälle. Diese als Page Objects bezeichneten Objekte repräsentieren typischerweise Seiten oder Seitenfragmente einer Anwendung und stellen die Interaktionsmöglichkeiten als Methoden zur Verfügung, zum Beispiel zur Eingabe von Daten.

Die Testfälle greifen nicht mehr direkt auf Selenium, sondern auf die Page Objects zu. Page Objects sollten selbstvalidierend sein, das heißt, sie sollten selbstständig prüfen, ob eine Aktion erfolgreich war. Idealerweise finden im eigentlichen Testfall-Quellcode also gar keine Aufruf der Webdriver-Schnittstelle statt, sondern es wird nur auf die Methoden der Page Objects zugegriffen. Führt ein Methodenaufruf auf einem Page Object zu einer Navigation zu einer neuen Seite, so gibt diese Methode einer Instanz des entsprechenden Page Objects zurück.

Die durch die Page Objects bereitgestellte Funktionalität kann von vielen Testfällen genutzt werden. Da die Interaktion nun gekapselt und nur genau einmal implementiert ist, verbessert sich  die Wartbarkeit enorm. Die eigentlichen Testfälle werden sehr gut lesbar, da sie kompakt sind und die Methoden der Page Objects typischerweise bedeutungstragende Namen habe.

Mit der PageFactory[2] bietet das Webdriver-API für Java/C#  eine nützliche Klasse zur Initialisierung von Page Objects zur Laufzeit an. Mittels der Factory können Instanzvariablen der Page Objects, die Weboberflächenelemente repräsentieren, vorbelegt werden, indem diese auf der aktuelle Seite mit den angegebenen Selektoren gesucht werden.

Oberflächenelemente zuverlässig finden

Einen großen Einfluss auf die Stabilität eines Testfalls hat die verwendete Technik zum Auffinden von Oberflächenelementen. In Selenium muss zur Interaktion mit einem Oberflächenelement, zum Beispiel einem HTML-Button, dieses zunächst im DOM-Baum der aktuellen Webseite aufgefunden werden.

Ideal ist hier die Verwendung des ID-Attributes oder Name-Attributes des HTML-Elements, da diese Attribute unabhängig von Design und Struktur der Webseite sind. Leider erzeugen viele Webframeworks die Nutzeroberflächenelemente dynamisch bzw. erzeugen eine dynamisches ID-Attribut. Der Entwickler kann somit die ID und den Namen eines Elements zur Laufzeit gegebenenfalls nicht beeinflussen und damit im Testfall nicht verwenden.

Eine Alternative ist die Nutzung von strukturellen Selektoren mittels XPath oder CSS. Hier ist allerdings Vorsicht geboten. Schnell ist ein Ausdruck nicht mehr gültig, weil die Struktur der Webseite oder Testdaten geändert wurden („suche den Button in der vierten Spalte der zweiten Reihe der dritten Tabelle…“).

Hat ein Element eine CSS-Klasse, sollte diese im Selektorausdruck referenziert werden, da die Klassennamen oft eine anwendungsspezifische Bedeutung haben. Das verbessert die Lesbarkeit des Testquellcodes und führt auch zu einer höheren Stabilität bei strukturellen Anpassungen an der Seite. Weil die CSS-Klassen sowohl von Entwicklern als auch Testern genutzt werden, ist die Wahrscheinlichkeit von Änderungen, die unbeabsichtigt zu nicht mehr lauffähigen Testfällen führen, geringer.

Wenn möglich, sollte CSS gegenüber XPath bevorzugt eingesetzt werden. Das Auffinden mittels CSS  ist (insbesondere in älteren Versionen des Internet Explorers) deutlich schneller als mit XPath und erhöht somit die Geschwindigkeit der Testausführung.

Die Diagnose von Problemen beim Auffinden von Oberflächenelementen erleichtert die Fähigkeit von Selenium, Screenshots des aktuellen Browserinhalts zu erzeugen. Wird Selenium zusammen mit JUnit 4 genutzt, kann zum Beispiel in Verbindung mit einem JUnit TestWatcher bei einem fehlgeschlagenen Testfall automatisch ein Screenshot erzeugt werden[3]. So kann schnell festgestellt werden, ob das gesuchte Element überhaupt auf der aktuellen Seite existiert.

Zusammenfassung

In diesem Artikel habe ich einige Tipps für die Erstellung von gut wartbaren automatisierten Testfällen für Webanwendungen mit Selenium 2 gegeben. Haben Sie weitere Hinweise zur Umsetzung von Selenium-Testfällen oder Fragen? Ich freue mich auf Ihre Kommentare!

Übrigens: mit der Test Factory bietet adesso die kosteneffiziente Erstellung von automatisierten Testfällen nach Best Practices als Dienstleistung an. Sprechen Sie mich gern an!

Arne-Michael Törsel Arne-Michael Törsel ist Softwareentwickler bei adesso und interessiert sich besonders für Themen rund um das Testen von Software.
Artikel bewerten:
1 Star2 Stars3 Stars4 Stars5 Stars
Loading...

Kommentare

Rocketlab – Test Automation 20. November 2015 Website des Autors

Super – sehr guter und klar verständlicher Artikel!

Kommentar hinzufügen:

Ihr Kommentar: