Über unsMediaKontaktImpressum
Dr. Jeremias Rößler 07. März 2017

5 Dinge, die Sie über Regressionstests wissen sollten

Automatisierte fachliche UI-Tests als Regressionstests funktionieren in der Praxis nicht sehr gut. Diese Tests sind teuer in der Erstellung, durch den konstant hohen Wartungsaufwand eine Bürde im Projekt, dabei aber brüchig und liegen im Nutzen irgendwo zwischen unzuverlässig und irreführend – und sind in ihrer Funktion zur Sicherung der Qualität trotz allem unvollständig. Das ist wahrscheinlich der Grund, warum in der Praxis laut dem letzten World Quality Report nur ca. 28 Prozent aller UI-Tests automatisiert sind [1]. Und das alles nur, weil wir Regressionstesten falsch verstanden haben und falsch anwenden!

Alle Welt spricht über Continuous Delivery und Shift Left. Das unausgesprochene und doch offensichtliche Problem besteht jedoch darin, dass es hierzu einer wesentlich höheren Testautomatisierung bedarf, als diese in den meisten Unternehmen in der Praxis anzutreffen ist. Und nicht jeder ist Google oder Facebook und kann es sich leisten, dass ein paar Tausend Benutzern kurzzeitig ein Fehler angezeigt wird. Das bedeutet, bevor der große Traum der kontinuierlichen oder zumindest regelmäßigen Lieferung gelebt werden kann, muss sich beim Thema Testing etwas gravierend ändern – und dazu bedarf es eventuell anderer Ansätze.

Sie kennen sicherlich die gängigen Tools zum Aufzeichnen bzw. "skripten" von fachlichen UI-Regressionstests. Seien dies nun Open Source-Tools oder kommerzielle Alternativen – sie funktionieren eigentlich alle nach dem gleichen Prinzip: Ein Test enthält im Wesentlichen drei Elemente: Anweisungen zur Simulation von Nutzeraktionen, Anweisung zur Ergebnisprüfung und Anweisungen zur Definition der Wartezeit zwischen den beiden. Da Nutzeraktion und Ergebnisprüfung sich immer auf einzelne UI-Elemente beziehen (z. B. Button klicken, Textfeld prüfen), werden diese UI-Elemente über Attribute wie Beschriftung, Name, Pixelkoordinate, etc. identifiziert. Und genau das ist die Wurzel allen Übels.

Um einen Test zu skripten, muss man nun für jede Prüfregel diese Identifikationsmerkmale festlegen, was manueller Aufwand ist. Hat man beispielsweise eine Maske mit 50 Eingabefeldern – und gerade mit Tabellen ist das schnell passiert – dann muss man passend 50 Identifikationsmerkmale festlegen, plus passender Ergebnisprüfung. Will man nun 10 Tests schreiben, sind das schon 500 Identifikationsmerkmale plus Prüfregel. Selbst wenn man pro Feld nur zwei Minuten braucht, sind das schon gut 16 Stunden Arbeitszeit. Kein Wunder also, dass man in herkömmlichen Tests nur das Nötigste überprüft – wodurch die Tests wiederum unvollständig sind. Natürlich kommt hier der Entwickler in uns sofort mit guten Einfällen, wie man das abstrahieren und vereinfachen kann und viele Ideen sind in den zahlreichen Werkzeugen auch umgesetzt – aber damit wird das Problem nur vereinfacht, nicht gelöst.

Fachliche Tests von Fachanwendern

Ein weiteres häufiges Problem besteht darin, dass man einen Fachanwender braucht, um den Test festzulegen – aber einen Programmierer oder mindestens einen geschulten Tester, um ihn zu codieren oder zu "skripten". Viele Tools gehen hier schon einen weiten Weg um dies zu erleichtern und zu vereinfachen und häufig kann man die jeweilige Skript-Sprache in wenigen Tagen lernen – wenn man ein Programmierer ist. Für echte Fachanwender ohne technisches Interesse hingegen stellt dies häufig eine gewisse Herausforderung dar.

Warum Regressionstests keine fachlichen Tests sind

Zweck und Nutzen eines fachlichen Tests ist das Aufdecken fachlicher Fehler im Code. Nach der Durchführung des Tests können wir hinreichend sicher sein, dass die Funktionalität, die im Test überprüft wird, auch entsprechend implementiert wurde. Fachliche Tests sind zwingend notwendig, wenn eine Funktionalität hinzugefügt oder verändert wurde. Doch sind fachliche Tests auch gute Regressionstests?

Ein Regressionstest hat eine andere Zielsetzung als ein fachlicher Test. Im Gegensatz zu einem fachlichen Test will ein Regressionstest nämlich keine Fehler finden! Wenn der Code einen fachlichen Fehler beinhaltet, die Software aber beim Kunden bereits eingesetzt wird, dann ist es häufig so, dass der Fehler nicht behoben werden darf. Es mussten schon Fehler wieder eingebaut werden, nachdem diese ohne Rücksprache mit dem Kunden einfach gefixt wurden...

Wenn aber ein Regressionstest keine Fehler findet, was tut er dann? Das Ziel eines Regressionstests ist fachliche Konsistenz. Der Regressionstest stellt sicher, dass nach einer Anpassung die ungeänderten Teile der Software noch so funktionieren wie vor der Anpassung – ungeachtet ihrer fachlichen Richtigkeit.

Sie kennen sicherlich den Spruch: Wenn man als einziges Werkzeug einen Hammer hat, dann sieht alles aus, wie ein Nagel. Die Tatsache, dass wir fachliche Tests als Regressionstests verwenden, ist allein der Tatsache geschuldet, dass wir keine Toolunterstützung für das haben, was wir eigentlich bräuchten: Konsistenzprüfung. Fachliche Tests "fixieren" das Verhalten der Anwendung. Das ist gut, wenn sich das Verhalten der Anwendung nicht ändern soll. Tatsächlich soll sich das Verhalten der Anwendung aber dauernd ändern. Und genau dann werden diese "fixen" Tests schnell von einer Hilfe zum Ballast. Was wiederum genau der Grund für die Form der allseits bekannten "Testpyramide" ist.

Was wir eigentlich bräuchten

Um nach einer Änderung fachliche Regressionen zu erkennen, benötigen wir eigentlich keine fachlichen Tests. Es reicht völlig aus, Änderungen zu erkennen – ähnlich einem Versionsverwaltungssystem. Den aktuellen Zustand als korrekt oder zumindest als gegeben zu akzeptieren und basierend darauf seine Tests zu definieren, ist keine neue Erkenntnis. Michael Feathers beschreibt diesen Ansatz in seinem Buch "Working effectively with legacy code" und nennt ihn dort "Characterization-Test" [2]. Dieses reine "Erkennen von Änderungen" reicht jedoch nicht aus. Was ein Versionsverwaltungssystem so effektiv macht, ist die Möglichkeit, diese Änderungen auch einfach zu "committen", also zu übernehmen. Und genau diese Anforderung brauchen wir auch für unser Regressionstestsystem.

Das Ende von Git

Wenn man eine Code-Änderung reviewt, muss man als Entwickler mehrere Aspekte bewerten: die Code-Qualität, die Änderungen an der Struktur des Codes... aber auch die Auswirkungen auf das Verhalten der Software. Das wiederum ist eine gedankliche Transferaufgabe. Und die Praxis zeigt, dass Menschen hierbei regelmäßig versagen. Bekannte Versionsverwaltungssysteme wie Git und SVN sind großartige Werkzeuge, um Änderungen auf Dateieben zu erkennen und damit statischen Code zu verwalten. Aber ihr Kompetenzbereich endet beim dynamischen Verhalten der Software. Um diese bekannte Lücke zu füllen, verwendet man normalerweise automatische Tests. Sie beantworten die Frage, wie eine Änderung das Verhalten der Anwendung beeinflusst und verändert. Unit-Tests definieren dieses Verhalten relativ "nahe" am Code. UI-Tests dagegen sind sehr weit weg von den jeweiligen Funktionen – was bewirkt, dass sich die Änderungen aufsummieren. Dadurch haben viele Änderungen Auswirkungen auf die UI, wodurch sich diese quasi "ständig" ändert.

Ach wie gut, dass keiner weiß, dass ich LoginButton heiß!

Da sich die Attribute von UI-Elementen ändern können, ist es wichtig, möglichst stabile Attribute zu wählen und diese auch noch über einzelne Tests hinweg wiederzuverwenden. Genau dies wird bspw. im mittlerweile bekannten Page Objects-Pattern oder beim Key-Word-Mapping getan und von Werkzeugen wie z. B. Cucumber propagiert. Ändert sich dann ein Attribut, so muss es nur an einer Stelle angepasst werden, womit wir den Wartungsaufwand für Aktionsziele und Prüfregeln minimieren können. Damit das funktioniert, wird aber relativ viel manueller Aufwand im Vorfeld betrieben, um die Abstraktionen zu erstellen. Wie bereits erwähnt, wird dadurch die Auswirkung des Problems nur reduziert, das Problem an sich aber nicht gelöst.

Die Versionsverwaltung für das Verhalten

Wie wäre es mit einer neuen Idee, gleichermaßen radikal wie einfach? Statt mit fachlichen Tests das korrekte Verhalten der Anwendung manuell zu spezifizieren, zeichnen wir einfach das aktuelle Verhalten der Anwendung auf. Beim "Abspielen" der Tests wird dann einfach das aufgezeichnete mit dem gegenwärtigen Verhalten verglichen und alle Unterschiede dem Anwender präsentiert. An dieser Stelle sollte erwähnt werden, dass das Aussehen der Anwendung, ebenso wie alle angezeigten Informationen Teil des Verhaltens sind. Der Anwender hat dann wie bei einem herkömmlichen Versionsverwaltungssystem prinzipiell drei Optionen: er kann das Verhalten gutheißen und bestätigen, er kann es ablehnen oder er kann es dauerhaft ignorieren. Bestätigt er das neue Verhalten der Software, entspricht das einem Committen im Versionsverwaltungssystem: Das Verhalten wird übernommen, abgespeichert und zukünftig wird gegen dieses Verhalten geprüft. Lehnt er das neue Verhalten ab, so bedeutet dies einfach, dass das System seinen Zweck erfüllt und einen Fehler entdeckt hat. Dieser muss dann behoben werden. In seltenen Fällen gibt es noch einige wenige UI-Elemente, die sich ständig ändern: Datumsanzeigen, Versionsnummern, etc. Diese können einfach dauerhaft vom Vergleich ausgeschlossen werden (ähnlich temporären Dateien oder Log-Dateien in Versionsverwaltungssystemen).

Wenn Redundanz etwas Gutes ist

Bei herkömmlichen GUI-Testwerkzeugen konzentriert man sich möglichst auf ein stabiles Attribut. Ändert sich dieses, so bricht der Test und man muss es nachpflegen. Würde man mehrere Attribute auf einmal wählen, so müsste man jedes Mal pflegen, wenn sich nur ein Attribut davon ändert. Wenn das Nachpflegen jedoch keinen Aufwand bedeutet, dann bedeutet die Identifikation von UI-Elementen über mehrere Attribute plötzlich, dass die Tests robuster werden! Ändert sich ein Attribut, kann das UI-Element immer noch über die verbleibenden Attribute korrekt identifiziert werden. Die Frage ist also, wie man das Nachpflegen effizient gestalten kann.

Der Puzzle-Vorteil

Stellen Sie sich vor, Sie haben zweimal das gleiche Puzzle. Nur eines davon hat Ihr Kind in die Hände gekriegt und hat das Bild komplett übermalt. Jetzt vergleichen Sie ein einzelnes Puzzlestück des originalen Puzzles und wollen das zugehörige veränderte und übermalte Puzzlestück finden. Wahrscheinlich keine leichte Aufgabe. Vergleichen Sie jedoch das komplette originale Puzzle mit dem kompletten veränderten Puzzle, wird die Aufgabe wesentlich vereinfacht. Genauso verhält es sich beim Regressionstest: Wenn Sie alle Elemente des vorherigen Zustandes kennen, ist es um ein vielfaches einfacher, eine 1-zu-1-Zuordnung zu machen und zu schauen, welche Elemente "übrigbleiben". Damit werden die Tests nochmals robuster gegen Änderungen.

Was soll denn eigentlich passieren, wenn man auf diesen Knopf drückt?

Dokumentation des Ist-Zustandes

Diese Art zu Testen birgt noch einen weiteren Vorteil: Der Ist-Zustand wird minutiös dokumentiert und belegt. Das ist nicht nur sinnvoll, wenn Sie eine Software für medizinische Geräte entwickeln, sondern auch in vielen anderen Fällen. Agile Methoden wollen den Wasserkopf in der Softwareentwicklung abbauen, indem sie Entwickler und Anwender an einen Tisch holen. Wenn dieser Ansatz gut funktioniert, bedeutet dies im Idealfall, das gar nichts mehr dokumentiert werden muss... was dann auch häufig der Fall ist. Wenn sich aber nach sechs Monaten die Anforderungen und die Leute im Team geändert haben und für ein neues Feature der Rest der Anwendung (meist manuell) getestet werden muss, stellt sich häufig die Frage, wie der Soll-Zustand eigentlich genau aussieht. Was soll denn nun eigentlich genau passieren, wenn man auf diesen Knopf drückt? Welche Daten sollten denn in diesem Feld eigentlich angezeigt werden? Wenn Sie sich diese Fragen stellen, dann dürfen Sie sich zu einem sehr schlanken und (hoffentlich) effizienten Softwareentwicklungsprozess beglückwünschen. Und für die Testautomatisierung sollten Sie gleich einen neuen Testing-Ansatz ausprobieren.

Wieso erst jetzt?

Wenn es so eine gute Idee ist, auf diese Art zu testen, warum machen wir das nicht schon lange? Nun, einer der Gründe besteht in der Tool-Unterstützung. Damit dieser Ansatz nicht Unmengen von "false positives" hervorbringt – also von Fehlern, die eigentlich gar keine Fehler sind –, muss eine Bedingung zwingend und soweit möglich erfüllt werden: die Wiederholbarkeit der Ausführung. Jede Änderung, und sei es nur die Uhrzeit, führt potenziell zu Änderungen, die keine sind. Je nach Situation und Komplexität der Testsysteme war das früher ein Problem. Heute haben wir Docker und Konsorten – das Aufbauen einer stabilen Testumgebung ist damit wesentlich einfacher, als noch vor wenigen Jahren. Ein zweiter Punkt der hinzukommt, ist die Verfügbarkeit von Rechenkapazität. Früher liefen die Rechner meist mit einer komplexen Anwendung am Rande ihrer Möglichkeiten – heute ist es kein Problem, eine Virtualisierung einer Simulation eines Containers in einer Virtualisierung in der Cloud laufen zu lassen. Damit kann man mehr und günstiger GUI-Tests automatisieren als jemals in der Geschichte des GUI-Testens.

Wohin die Reise führt

Die meisten Menschen sind sich einig, dass die Zukunft des Testens (so wie die Zukunft von quasi allem anderen) in der künstlichen Intelligenz liegt. Die Frage ist daher nicht, wie die Zukunft des Testens aussieht – die Frage ist, wann diese Zukunft kommt und wie der Weg dahin aussieht. Da ein fachlicher Fehler nur im Auge des Betrachters liegt und damit sehr stark kontextabhängig ist, kann fachliches Testen erst durch eine "starke" KI, also eine menschenähnliche und allgemeingültige Intelligenz, übernommen werden. Diese Herausforderung wird in der Forschung "Orakel-Problem" genannt. Wenn wir jedoch Tests ohne Prüfregeln festlegen, bei denen rein der aktuelle Stand dokumentiert und mit einer späteren Version verglichen wird, dann muss eine KI plötzlich nicht mehr "stark" sein. Sie muss einen fachlichen Fehler nicht mehr als solchen erkennen, dass macht dann nach einer Änderung der Anwender. Die KI muss nur noch die Anwendung ausführen und möglichst alle Anwendungsfälle inklusive Ausnahmen und Sonderfällen aufzeichnen. Und dazu kann man eine KI sogar heute schon trainieren!

Die ReTest GmbH ist ein in Karlsruhe ansässiges Start-up, welches sich mit KI-basiertem Testen beschäftigt und ein Werkzeug für einen neuen Testing-Ansatz entwickelt, welchen wir "Difference-Testing" nennen. Wir haben gerade die Version 1.0 veröffentlicht und sind nun dabei, Teile unseres Tools als Open Source zu veröffentlichen. Wir würden uns freuen, wenn Sie uns besuchen, um unser Werkzeug herunterzuladen, auszuprobieren und uns Feedback zu geben. Oder besuchen Sie uns auf der CeBIT in der Halle 7 am Stand D13.

Die Zeit ist reif, einen neuen Testing-Ansatz auszuprobieren!

Quellen
  1. World Quality Report 2016-17
  2. M. Feathers, 2013: Working effectively with legacy code, Prentice Hall

Autor

Dr. Jeremias Rößler

Dr. Jeremias Rößler ist Software-Ingenieur und Experte für die Erstellung und Pflege großer, komplexer Softwaresysteme. 2013 hat er am Lehrstuhl für Softwaretechnik an der Universität des Saarlandes promoviert.
>> Weiterlesen
Das könnte Sie auch interessieren
Kommentare (0)

Neuen Kommentar schreiben