End-to-End-Testautomatisierung in großem Maßstab bedeutet, eine stabile, wartbare automatisierte Testsuite für große, langlebige Anwendungen zu erstellen, ohne dass dabei Instabilitäten und Rückstände der Wartung entstehen. Sie basiert auf drei Säulen: der präzisen Identifizierung von UI-Elementen durch spezielle HTML-Testattribute, der bewussten Auswahl einer kleinen Anzahl von Testfällen mit hoher Überdeckung und einer mehrschichtigen Framework-Architektur, die wiederverwendbare Logik zentralisiert und einheitliche Namenskonventionen durchsetzt.
Das Wichtigste in Kürze
- Ein eigens zu Testzwecken hinzugefügtes benutzerdefiniertes HTML-Attribut, das 2017 eingeführt wurde, sorgt auch Jahre später noch für eine stabile, fehlerunempfindliche Elementidentifizierung in einem großen Anwendungsportfolio der öffentlichen Verwaltung.
- Die Eingrenzung des DOM, sodass für das Testtool nur der relevante Abschnitt vorhanden ist, beseitigt Unklarheiten, wenn identische Elemente in mehreren Abschnitten vorkommen, und ermöglicht so eine präzise Zielauswahl ohne komplexe Selektoren.
- Fünfzehn gut ausgewählte Testfälle, die die kritischsten Szenarien abdecken, liefern einen nachhaltigeren Nutzen als fünfhundert vage definierte, die die Ausführungszeit und die Wartungskapazität belasten.
- Workarounds für Wartezeiten sammeln sich unbemerkt an: Bei einem analysierten Projekt gingen pro Testlauf zwei Stunden durch einzeln betrachtet kleine, fest programmierte Wartezeiten verloren – ein Aufwand, der sich nur durch die Entwicklung eines ordentlichen Detektors für Anwendungsauslastung beseitigen ließ.
- Wenn jedes Automatisierungsverfahren als „Aktion plus Geschäftsbezeichnung“ benannt wird (zum Beispiel „Name eingeben“), kann ein neues Teammitglied den Testcode lesen und sich in der Anwendungslogik zurechtfinden, ohne dass eine separate Dokumentation nötig ist.
Testautomatisierung ist Softwareentwicklung, keine Nebenaufgabe
Testautomatisierung ist ein Softwareprojekt innerhalb eines größeren Projekts. Diese Sichtweise verändert die Art und Weise, wie man sie aufbaut: mit Architektur, Namenskonventionen, Entwurfsmustern und derselben Disziplin, die man auch beim Produktionscode anwendet.
Lilia Gargouri war fünfzehn Jahre lang in der Softwareentwicklung tätig, bevor sie in den Bereich Qualität und groß angelegte Testautomatisierung wechselte. Aus dieser Perspektive betrachtet ist die „Abkürzungsmentalität“ rund um die Testautomatisierung die Wurzel der meisten Probleme. Das schnelle Generieren von Testcode lässt sich nicht skalieren. Es treibt das Chaos nur noch weiter voran.
Die End-to-End-Ebene befindet sich an der Spitze der Testpyramide, und genau dort liegt die Ursache für Unzuverlässigkeit. Teams verschwenden routinemäßig einen großen Teil ihrer Automatisierungszeit mit Unzuverlässigkeit und Wartung. Die Probleme sind alt und bekannt, doch sie bestehen weiterhin. Wenn man ohne Strategie einfach KI darauf loslässt, vervielfachen sie sich noch schneller.
Warum Unternehmensanwendungen eine schwache Automatisierung bestrafen
Langlebige Unternehmenssoftware deckt jede Schwäche in einer Testsuite auf. Eine Anwendung, die 25 Jahre lang läuft, sich über Release-Zyklen weiterentwickelt und eine hohe geschäftliche Komplexität aufweist, bedeutet, dass sich alles gleichzeitig skaliert: die Anzahl der Tests, die Ausführungszeit und der Wartungsaufwand.
Lilia entwickelt Automatisierungslösungen für große E-Government-Projekte, bei denen dieser Druck ständig vorhanden ist. Neue Funktionen kommen hinzu, während bestehende gewartet werden müssen, und die Testsuite wächst in jeder Hinsicht. Ohne eine klare Strategie von Anfang an geraten Teams schnell in die Falle der Wartung und bleiben dort stecken.
Die Reihenfolge der Arbeitsschritte ist entscheidend. Man fängt nicht damit an, Tests zu generieren. Die Zuverlässigkeit der End-to-End-Automatisierung beginnt mit einer präzisen Elementerkennung, und die hängt von der Qualität des HTML-Codes ab. Die Erkennung nachträglich zu korrigieren, ist nur ein vorübergehender Notbehelf, keine Lösung.
Weltweit zielt der Großteil der Automatisierung immer noch auf Web-Benutzeroberflächen ab, was bedeutet, dass die größte Herausforderung darin besteht, ein winziges, spezifisches Element anzusteuern. Stell dir eine Tabelle mit fünfzig Lösch-Buttons vor, jeder davon ein kleines Symbol. Genau den gewünschten Button anzusteuern – genau hier versagen instabile Lokalisatoren.
Die dauerhafte Lösung ist ein spezielles HTML-Attribut, das ausschließlich zum Testen dient. Nenn es „Data-Role-Attribut“, „Test-ID“ oder wie auch immer es zu deinem Team passt. Es enthält einen Wert, der ausschließlich zur Erkennung im Test verwendet wird, ist in der gesamten Anwendung eine Anforderung und ändert sich nie. Fehlt es, ist das ein Bug-Ticket für den Entwickler.
Diese Entscheidung zahlt sich über Jahre hinweg aus. Lilias Team hat das Attribut 2017 eingeführt und profitiert noch immer davon. Automatisierung ist die Summe kleiner Entscheidungen, die sich zu Effizienz summieren.
Der zweite Baustein ist ein zentraler Mapper. Er ordnet jeden Attributwert einer Testautomatisierungsklasse zu, sodass das Tool eine Tabelle, eine Zeile, eine Zelle oder eine Symbolschaltfläche erkennt. Diese Zuordnung an einem Ort zu speichern bedeutet, dass auch die Implementierung und ihre Wartung an einem Ort erfolgen und beim Öffnen des Browsers sofort geladen werden.
Identität durch Attribute zur Zugänglichkeit anreichern
Attribute zur Zugänglichkeit dienen gleichzeitig als Bedeutungsquelle für Testcode. Da E-Government-Anwendungen blinde Nutzer bedienen müssen, sind Attribute wie aria-label und alt bereits vorhanden und von vornherein aussagekräftig.
Lilias Skripte extrahieren diese Informationen, um die Identität von UI-Elementen anzureichern. Der Nebeneffekt ist echte Überdeckung: Beim Testen wird implizit überprüft, ob die Werte der Zugänglichkeit für die Menschen, die sich darauf verlassen, Sinn ergeben. Code, der sich so liest wie die Benutzeroberfläche, die er steuert, ist später leichter zu verstehen.
Den Umfang einschränken, um die Zielgenauigkeit zu verbessern
Das Eingrenzen des DOM auf einen einzelnen Abschnitt ermöglicht eine präzise Zielausrichtung. Wenn sich ein Feldname über viele Abschnitte hinweg wiederholt, kannst du das fünfte Feld auf einer ganzen Seite nicht mit ausreichender Zuverlässigkeit treffen.
Die Technik besteht darin, den Rest des DOM für das Tool unsichtbar zu machen und nur den relevanten Abschnitt zu belassen. Mit einem kleinen Umfang zielst du präzise ab, die Testläufe werden stabil und Wiederholungen werden vertrauenswürdig. Stabilität ist das Ergebnis vieler solcher Entscheidungen zusammen.
Identifizierung über Labels für stabilen, generischen Code
Die Identifizierung über Labels sorgt dafür, dass Tests lesbar und – in diesem Zusammenhang – stabil bleiben. In Behördenanwendungen ändern sich die Labels von Eingabefeldern selten, sodass ein Locator wie das Textfeld mit dem Label „name“ über lange Zeit hinweg gültig bleibt.
Beschriftungen machen zudem die Mehrsprachenunterstützung kostengünstig. Trage die Übersetzungen der Beschriftungen in eine Tabelle ein, verweise generisch auf die Beschriftung, und dieselbe Codezeile läuft je nach Anmeldung auf Englisch, Italienisch oder in jeder anderen Sprache. Eine ID, eine Zeile, keine Duplikate.
Entwirf eine kleine Auswahl hochwertiger Testfälle
Weniger, aber gezieltere Ende-zu-Ende-Tests sind besser als eine große, redundante Testsuite. Lilia ist kein Fan von fünfhundert Testfällen. Fünfzehn, die die wichtigsten Verhaltensweisen abdecken, reichen aus, um eine ernsthafte Diskussion über Qualität zu beginnen: Wenn diese grün sind, kannst du über den Rest sprechen.
Stell dir jeden Testfall als einen Stein in einem Rucksack vor, den du jahrelang mit dir trägst. Ist der Rucksack schwer, leidet die Reise darunter. Also entscheidest du bewusst, was einen Platz verdient.
Drei Säulen bestimmen die Qualität eines Testfalls.
| Säule | Zu stellende Frage |
|---|---|
| Szenario | Was ist der Schwerpunkt, und ist er entscheidend? Kann ein Schritt entfernt werden, ohne dass die Überdeckung darunter leidet? |
| Testdaten | Welche Grenzen und Äquivalenzklassen haben echten Wert, und sind die Daten wiederverwendbar und wartbar? |
| Implementierung | Hält die Implementierung den Wartungsaufwand gering, egal ob manuell oder automatisiert? |
Reduziere jeden Testfall auf das Wesentliche. Ziehe nicht blindlings Fälle aus dem Testmanagementwerkzeug und automatisiere sie, ohne ihre Redundanzen zu beseitigen. Kläre den Schwerpunkt, streiche Schritte, die nichts beitragen, und wähle Testdaten, die tatsächlich etwas abdecken, statt überall „ABC123“ zu verwenden. Teure Automatisierung mit bedeutungslosen Daten bringt keinen Mehrwert.
Priorisiere das Backlog, bevor du irgendetwas automatisierst
Ein definiertes Backlog ist kein Signal, mit dem Programmieren zu beginnen. Es ist der Punkt, an dem du eine Reihenfolge wählst, die zu deiner Situation passt.
Prioritäten hängen von Strategie und Kontext ab. Vielleicht automatisierst du zuerst die geschäftskritischen Fälle, damit dir eine grüne Pipeline dort Sicherheit gibt, wo es darauf ankommt. Vielleicht konzentrierst du dich auf die Fälle, die manuelle Tester am meisten Zeit kosten, um sie für neue Funktionen freizustellen. Vielleicht deckst du zuerst die „Happy Paths“ oder die am häufigsten genutzten Abläufe ab. Die richtige Reihenfolge hängt vom Hier und Jetzt ab, nicht von einer festen Regel.
Ein dreischichtiges Framework sorgt für eine hohe Wartbarkeit der Automatisierung
Eine mehrschichtige Architektur sorgt dafür, dass die Automatisierung jahrelange Veränderungen übersteht. Die Struktur spiegelt das ISTQB-Modell für fortgeschrittene Automatisierung wider: Kernbibliotheken ganz unten, Geschäftslogik in der Mitte, Testskripte ganz oben.
Testfälle und Testsuiten befinden sich ganz oben und rufen Prozeduren auf, die die Geschäftslogik abwickeln. Die Geschäftslogik ruft die Bibliotheken auf. Jede Schicht hat eine Aufgabe.
Die Bibliotheken selbst sind geschichtet. Das Tool enthält eine Standardbibliothek, die jedoch nie ausreicht. Darüber befindet sich eine unternehmensweite, projektübergreifende Bibliothek für Funktionen wie das Wechseln von Browserfenstern, damit kein Projekt diese Funktion neu erfinden muss. Darüber liegt eine Projektbibliothek für wiederkehrende Interaktionen, wie zum Beispiel ein Kontextmenü hinter drei Punkten: Eine Prozedur übernimmt die gewünschte Aktion und führt sie aus. Spezialisierte Bibliotheken kümmern sich um E-Mail-Inhalte und PDF-Inhalte. Jedes Mal, wenn du etwas Neues entwickelst, lege es in die passende Bibliothek ab.
Eine Namenskonvention garantiert sauberen Code
Eine einheitliche Namensgebung ist der Mechanismus, der eine wachsende Testsuite lesbar hält. Die konkrete Konvention ist weniger wichtig als die konsequente Anwendung überall und jederzeit.
Lilias Konvention lautet: Aktion plus Geschäftsbezeichnung. Die Aktion ist „Eingabe“, „Markieren“ oder „Klicken“. Die Geschäftsbezeichnung ist die Beschriftung. „Name eingeben“ bedeutet also, einen Wert in das Feld mit der Beschriftung „Name“ einzutragen. Der technische Widget-Typ wird in der Bezeichnung nicht erwähnt: Ein Kontrollkästchen, ein Optionsfeld oder eine Datumsauswahl sind alle „Eingabe“ plus ihre jeweilige Beschriftung. Wenn du die Augen schließt, lässt dich der Code anhand der Geschäftsbezeichnung durch die Anwendung navigieren.
Lesbarer Code verkürzt die Einarbeitungszeit erheblich. Neue Teammitglieder, die zu Lilias Team stoßen, können absichtlich Wochen mit einem einzigen Testfall verbringen, bis sie die Konvention verinnerlicht haben und sie überall anwenden. Die Struktur der Automatisierung entspricht der Struktur der Benutzeroberfläche, sodass jeder, der sich in der Benutzeroberfläche zurechtfindet, sich auch im Code zurechtfindet.
Darin verbirgt sich eine wichtige Erkenntnis: Wenn du dich dabei ertappst, nach der Implementierungsstelle von etwas zu suchen, ist deine Klassifizierung nicht gut genug.
Wenn du dich dabei ertappst, nach etwas zu suchen – wo haben wir das implementiert und wo –, bedeutet das, dass dein System nicht gut genug ist. — Lilia Gargouri
Warum die Wartung gering bleibt: eine Änderung, eine Stelle
Zentralisierung und generischer Code halten die Kosten der Wartung bei Änderungen gering. Wenn sich etwas ändert, gibt es in einer gut strukturierten Suite im Idealfall nur eine Stelle, an der man das beheben muss.
Vergleiche das mal mit verketteten IDs und rohem XPath. Wenn so eine Zeile fehlgeschlagen ist, musst du sie erst aufteilen, um herauszufinden, welcher Teil kaputt ist, dann entscheiden, ob es ein Bug oder ein Feature ist, und das wiederholst du jedes Mal, wenn die Zeile fehlgeschlagen ist. Unter Release-Druck verlieren Teams genau dadurch ganze Tage.
Die zu fürchtende Fehlerauswirkung ist Skalierung ohne Struktur. Du suchst nach einer ID und findest fünfhundert Treffer, dann steht ein Release-Kandidat an, bei dem sich dieses Element als Feature geändert hat, und du musst schnell fünfhundert Befunde reparieren. Das ist eine tickende Zeitbombe.
Wie man die Ausführung stabilisiert und Zeitverschwendung vermeidet
Stabile Ausführung entsteht dadurch, dass man auf das richtige Signal wartet – nicht auf die Uhr. Wartezeiten summieren sich unbemerkt und lösen das eigentliche Problem nicht.
Verwende einen Anwendungs-Auslastungsdetektor anstelle von Wartezeiten. Er baut auf demselben Resolver auf, wartet, solange der Spinner sichtbar ist, und fährt fort, sobald die Anwendung bereit ist. In einem Projekt mit vierzig Testsuiten summierten sich allein die Wartezeiten auf zwei Stunden pro Durchlauf: zwei Millisekunden hier, zwei Millisekunden dort – völlig umsonst verloren.
Halte die Umgebung auf dem neuesten Stand, denn Versionen beeinflussen sich gegenseitig. Der Browser, Java, Gradle, das System unter Test und das Testtool verändern sich im Laufe der Zeit und müssen zusammenarbeiten, um stabil zu bleiben.
Isoliere den Testlauf. Auf dem Rechner oder im Container sollten keine anderen Jobs parallel laufen, sonst erbst du Nebenwirkungen, Leistungsschwankungen und Fehlerwirkungen, die nicht von dir stammen. Stell sicher, dass kein anderer Prozess auf dieselben Daten zugreift, die deine Tests verwenden, sonst bekommst du Ausnahmen, die dich auf die Jagd nach Phantomfehlern schicken.
Modulare Testfälle geben dir Flexibilität unter Druck
Modularität verwandelt eine starre Testsuite in etwas, das du nach Bedarf neu zusammenstellen kannst. Jeder Testfall ist eine eigenständige Einheit, der es egal ist, was davor oder danach läuft.
Diese Unabhängigkeit ermöglicht es dir, Testläufe anhand einer Auswirkungsanalyse zusammenzustellen: Wähle die Themen aus, die du brauchst, und führe sie gemeinsam aus. Testsuiten sind nach Geschäftsthemen getrennt, und wenn eine davon dein Zeitlimit überschreitet – in Lilias Fall eine Stunde –, teilst du sie reibungslos in Unterthemen auf. Du kannst dann Smoke-Tests, Integrationstests und funktionale Tests nacheinander ausführen und frühzeitig abbrechen, wenn die kritische Ebene fehlgeschlagen ist.
Der Nutzen zeigt sich in heiklen Momenten. Als ein Tool-Update die Electron-Variante einer Geschäftsanwendung lahmlegte, lief derselbe Testcode auf der Webversion weiterhin einwandfrei. Nur die Vorbedingung änderte sich: Es wurde ein Browser statt der Electron-App geöffnet. Innerhalb von fünf Minuten lieferten die Webtests funktionale Ergebnisse, während das Electron-Problem behoben wurde, und die Veröffentlichung wurde nicht blockiert.
Wie man eine alte, unzuverlässige Testsuite rettet
Die Rettung einer veralteten Testsuite beginnt mit einem parallelen Repository, nicht mit einer Neuprogrammierung an Ort und Stelle. Halte die alte Welt am Laufen, während du daneben die neue aufbaust, und wechsle irgendwann vollständig um.
Richte zuerst die saubere Struktur anhand einer Projektvorlage ein und implementiere dann darin neue Testfälle unter Einhaltung der Namenskonvention. Wenn du Prozeduren und Bibliotheken benötigst, hole dir die wertvollen Teile Stück für Stück aus der alten Testsuite heraus und baue sie als sauberen Code in der neuen Bibliothek neu auf. Lilia hat genau diese Situation als Entwicklerin übernommen und die neue Lösung Stück für Stück aus dem alten Chaos heraus entwickelt.
Die Migration braucht Zeit, und es lohnt sich trotzdem, damit anzufangen. Ein zehn Kilometer langer Spaziergang beginnt mit einem Schritt, und die Alternative ist ein weiterer Tag voller Unzuverlässigkeit und ein großer Teil deiner Arbeitszeit, der durch Wartung verloren geht. Weniger Wartung ist letztendlich eine Frage der Qualität des Lebens.


