Ein Konzept für die End-to-End-Testautomatisierung

Bewährte Werkzeuge und Praktiken helfen beim Erstellen und Pflegen erfolgreicher automatisierter Tests.

Lesezeit: 10 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen 17 Beiträge

(Bild: whiteMocca/Shutterstock.com)

Von
Inhaltsverzeichnis

Testen in produktionsähnlichen Szenarien klingt verlockend, doch es gibt eine Menge zu beachten. Ein gutes Konzept für automatisiertes Testen ist besonders wichtig.

Das Testen einer Software soll die Anwesenheit von Fehlern anzeigen. Keine Fehler zu finden bedeutet jedoch nicht, dass ein brauchbares System vorliegt [1]. Wie kann die Erfüllung der Akzeptanzkriterien dennoch über den gesamten Entwicklungszeitraum wiederholt und aussagekräftig getestet werden?

Für das Softwaretesting gibt es verschiedene Prüftechniken und Testverfahren. Als Beispiel dient das manuelle Testen oder das sogenannte End-to-End-Testing (E2E). Derartige Testverfahren zeigen Fehler an und stellen die angemessene Erfüllung der Anforderungen aus einer funktionalen Sicht der Anwender sicher.

E2E-Testing bietet die Möglichkeit, das gesamte Softwareprodukt anhand produktionsähnlicher Szenarien von Anfang bis zum Ende automatisiert zu testen, um sicherzustellen, dass sich der Anwendungsfluss wie erwartet verhält. Somit ist das E2E-Testing ein benutzernahes Testvorgehen und eine gute Alternative zum manuellen Testen, vorausgesetzt das E2E-Testing wird adäquat umgesetzt.

Im Vergleich zum manuellen Testen werden E2E-Tests automatisiert und wiederholt ausgeführt, erfordern keinen geschulten Tester oder menschliches Eingreifen und lassen sich im gesamten Entwicklungsprozess des Softwareprodukts gut integrieren. Jedoch herrscht die Meinung vor, dass eine gute E2E-Testabdeckung in der Praxis kaum umsetzbar ist und die Automatisierung der Testfälle viel Zeit in Anspruch nimmt. Solche Aussagen lassen sich mit einem gut erarbeiteten Konzept relativieren.

Im Folgenden stellt der Artikel eine Sammlung praxisnaher, technischer und organisatorischer Guidelines dar und erläutert bewährte Werkzeuge, die sowohl die Entwicklung als auch die Pflege automatisierter E2E-Tests erleichtern und vereinfachen.

Dargestellte Guidelines sind möglichst voneinander getrennt zu betrachten und bieten als Gesamtheit eine gute Basis für das automatisierte Testen. Somit lassen sie sich am Projektanfang sowie in einem laufenden Projekt umsetzen.

Um die Aspekte so praxisnah wie möglich zu erläutern, wird eine Software als Beispiel behandelt, die aus einer Bedienoberfläche (fortan die Anwendung) und den Backendsystemen (fortan die Umgebung) besteht. Die Anwendungs- und die Umgebungsentwicklung sind voneinander unabhängig.

Eine gut durchdachte Struktur ist für die Entwicklung eines Tests notwendig. Sie trennt die grafische Benutzeroberfläche (UI), Darstellung, Logik und Testdaten voneinander, erleichtert die parallele Entwicklung und macht einen Code wesentlich wartbarer, als einen Boilerplate-Code – also Codeabschnitte, die an vielen Stellen in mehr oder weniger unveränderter Form benötigt werden.

Beispielhaft werden im weiteren Verlauf eine einfache Login-Seite mit zwei Textfeldern für Benutzername und Passwort sowie einen Button für den Login und einen weiterern für die Registrierung getestet.

Die Struktur für diesen Test besteht aus fünf Elementen (s. Abb. 1). Eine abstrakte Klasse (AbstractPage) realisiert die UI-Darstellung als Oberklasse, von der alle Anwendungsseiten erben. Das erfolgt mit dem Page-Object-Pattern (weitere Informationen s.u.).

Für die Entwicklung eines Tests ist eine durchdachte Struktur notwendig (Abb. 1).

Die einzelnen Testschritte werden in Klassen gegliedert. Sie stellen Methoden zur Verfügung, die komplette Prozesse oder Teilprozesse wie das Ausfüllen eines Formulars für die Eingabe von Login-Daten anbieten. Eine Testschritte-Klasse kann in diesem Beispiel die Methode anmelden_als(nutzer: string) enthalten.

Die zur Verfügung gestellten Methoden lassen sich in den Testfällen nutzen und mit Testdaten befüllen.

Die Struktur für den Beispieltest umfasst auch Testdaten. Die in den Testfällen benutzten Testdaten werden in gesonderten Klassen gepflegt.

Das Zusammenspiel zwischen Testdaten und Testschritten, dem Input und der Validierung des Outputs, findet in einem Testfall (Element vier) statt. In dem Ordner befinden sich die Testfälle so sortiert, wie die Prozesse in der Anwendung gruppiert (bspw. Lieferantendaten, Auftragsmanagement) sind.

Testfälle können auf diesem Weg für einzelne Bereiche eine Testsuite – eine Sammlung von Testfällen, die beispielsweise eine fachliche oder technische Gemeinsamkeit haben – bilden und später als eine Einheit durchgeführt werden.

Unter Utils sind Hilfsklassen zu finden, die beispielsweise die Anwendung starten und schließen.

Die Einführung von OOP-Konzepten, insbesondere Klassen, Vererbung und Polymorphie, erhöht nicht nur die Wiederverwendbarkeit, sondern auch die Wartbarkeit und die Lesbarkeit des Testfall-Codes. In dieser Hinsicht ist die Umsetzung des Page-Object-Pattern empfehlenswert. Eine Page-Object-Klasse umschließt eine HTML-Seite mit einer anwendungsspezifischen API, sodass es mit den UI-Elementen interagieren kann, ohne direkt den HTML-Code zu benutzen. Eine solche Klasse besteht aus drei Teilen: aus einer passenden Benennung für das UI-Element (z. B. loginButton) sowie aus einem Selektor. Jedes UI-Element hat eigene Merkmale, die es von anderen UI-Elementen unterscheidet. Die Merkmale können beispielsweise die Attribute Id oderdata-id, die Positionierung eines Elements im DOM-Baum einer HTML-Seite, bestimmte CSS-Klassen oder ein Text sein. In diesem Artikel ist die Abfrage gemeint, die das Element auswählen lässt.

Zu guter Letzt enthält die Page-Object-Klasse mögliche Interaktionen mit den UI-Elementen (z.B. loginbutton anklicken). Methoden für andere Klassen (Testschritte) lösen die Interaktionen aus.

Die investierte Zeit der Modellierung und Benutzung von Page-Object-Pattern lohnt sich spätestens dann, wenn eine UI-Änderung stattfindet oder gewisse zentrale Datensätze angepasst werden sollen. Anstatt an diversen Stellen eine Anpassung vorzunehmen, genügt meistens die Änderung an einer Stelle.

Die Anwendung nützlicher Werkzeuge wie Entwurfsmuster bieten erprobte Lösungen für wiederkehrende Probleme. Beispielsweise durch das Benutzen von Fluent Interfaces lassen sich die Testdaten an eine Methode als einzelner Parameter übergeben, anstatt die Objekterstellung hinter einer Methode zu verbergen, die mehrere Parameter entgegennimmt. Solche sprechenden Schnittstellen erhöhen die Lesbarkeit des Codes enorm.

Sowohl der Hauptstrang eines Prozesses einer umgesetzten Anforderung, als auch die Abweichungen, sollten getestet werden. Product Owner, Testmanager und Entwicklungsteam entscheiden gemeinsam darüber, was für ein Testtyp sich am besten für die Überprüfung der Umsetzung einer Anforderung eignet.

Ein "Happy Path"-Testfall ist das einfachste Szenario, in dem ein Prozess funktionieren soll, ohne dass Ausnahmen oder Fehlerzustände auftreten – sollte jedoch als ein E2E-Test automatisiert werden.

Durch die Abnahme der Story zeigen sich wahrscheinlich Verhaltensabweichungen. Wenn sich solche als Fehler erweisen und man sie behoben hat, lässt sich die Beseitigung des Fehlers durch einen E2E-Testfall prüfen.

Im Idealfall wird für die Ausführung der E2E-Tests eine neu erstellte und für die Entwickler nicht zugängliche Testinfrastruktur aufgesetzt und die Tests laufen gegen eine möglichst unangetastete Umgebung.

Da das Vorgehen meistens aufgrund des Bauens der Umgebung viel Zeit in Anspruch nimmt, favorisiert man in der Praxis den Einsatz einer bereits gebauten Umgebung, auf der eventuell mehrere Teammitglieder testen und Änderungen vornehmen können. Dieses Vorgehen ist jedoch nicht zu empfehlen. Es führt meistens zu inkonsistenten Testergebnissen, da die verschiedenen Nutzer die Datenbasis insoweit verfälschen, dass sie nicht zu den erwarteten Testergebnissen führt.

Mit Virtualisierungssoftware wie Docker lässt sich eine Testinfrastruktur hoch- und nach Abschluss der Testaktivitäten herunterfahren. Die Tests können im Anschluss isoliert und mit einer definierten Datenbasis im Docker-Container laufen. Auf diese Weise wird der Ausgangszustand der Testinfrastruktur wiederholt auf identische Weise hergestellt und die gelieferten Ergebnisse gewinnen an Aussagekraft.