Beiträge

Lasst die Affen testen – Monkey-Testing in der UI-Testautomatisierung

Kann man Benutzeroberflächen (UI) ohne Testfälle automatisiert testen? Ganz klar, ja.

Mit Monkey-Testing können Benutzeroberflächen ab der ersten Version automatisiert getestet werden. Die Robustheit der Software wird von Anfang an deutlich gesteigert, ohne einen einzigen Testfall zu erstellen. Allerdings gibt es Grenzen. Wie das funktioniert und wo die Grenzen von Monkey-Testing liegen, klärt dieser Beitrag.

Der lange Weg zum automatisierten Benutzeroberflächentest (UI-Test)

In Software-Projekten stehen automatisierte UI-Tests oftmals (wenn überhaupt) erst spät zur Verfügung.

Die Gründe hierfür sind vielschichtig. Um ein UI-Testautomatisierungssystem programmieren zu können, sollten die Anforderungen an die Benutzeroberfläche bekannt sein, ein erstes Bedienkonzept inkl. Formularentwürfen vorliegen, die Rahmenbedingungen des Testumfanges festgelegt und ein grundlegendes Architekturverständnis des Testsystems vorhanden sein.

Selbst in agilen Projekten werden die hohen Initialaufwände zum Aufbau eines UI-Testautomatisierungssystems auf spätere Iterationen verschoben, obwohl die Anforderungen (z.B. User Storys) und Akzeptanz Kriterien frühzeitig vorliegen. Bis die UI-Testautomatisierung einsatzbereit ist, verzichtet das Projektteam wissentlich auf die Möglichkeit eines regelmäßigen Feedbacks über die erreichte Qualität. Stattdessen versucht man, das Fehlen automatisierter UI-Tests durch verstärkten Einsatz manueller Testverfahren (z.B. exploratives Testen) zu kompensieren.

Wo liegen die Stärken des Monkey-Testings?

Monkey-Testing benötigt nur wenige Informationen über die Benutzeroberfläche. Somit können die Monkeys ab der ersten testfähigen Version losgelassen werden. Das Monkey-Testing steigert von Beginn an die Software-Qualität, da die Robustheit der Benutzeroberfläche ohne manuellen Aufwand automatisiert getestet wird.

Mit Monkey-Testing schließt sich die Lücke zwischen der ersten testfähigen Version und der meist später verfügbaren UI-Testautomatisierung.

Auch nach Einführung einer UI-Testautomatisierung spielt Monkey-Testing seine Stärken aus. Oftmals ist die UI-Testautomatisierung lückenhaft, da Programmänderungen noch nachgezogen werden müssen oder zu testende Funktionalitäten noch nicht umgesetzt wurden.

Was ist Monkey-Testing?

Monkey-Testing ist ein zufallsbasiertes Testverfahren, das auf dem Infinite-Monkey-Theorem basiert. Dieses besagt, dass ein Affe, der lange genug auf einer Schreibmaschine rumtippt, irgendwann die vollständigen Werke von William Shakespeare erzeugt hat. Überträgt man dies sinngemäß auf den Softwaretest, müsste ein Affe, der lange genug mit einer Benutzeroberfläche interagiert, sämtliche Funktionen durchlaufen und alle vorhandenen Fehler aufdecken.

Soviel zur Theorie – in der Praxis hat diese Behauptung nicht allzu lange bestand. Weder gibt es Affen, die auf Testrechnern herumtippen, noch gibt es fehlerfreie Software. Selbst wenn man dieser Idealvorstellung sehr nahe kommen möchte, würde ein Affe bzw. ein Testsystem, das diesen simuliert, immer noch sehr, sehr viel Zeit brauchen.

Wann ist Monkey-Testing sinnvoll?

Der größte Vorteil von Monkey-Testing liegt in der frühen Verfügbarkeit und dem geringen Aufwand für das Setup der Testinfrastruktur. Ein moderat intelligentes Monkey-Testing-System lässt sich innerhalb weniger Stunden installieren und konfigurieren (zumindest mit unserem Monkey-Testing-Werkzeug). Monkey-Testing kann daher zum Einsatz kommen, sobald ein lauffähiger Stand der Benutzeroberfläche vorliegt. Dies ist in den meisten Projekten zu einem Zeitpunkt der Fall, wo automatisierte funktionale Tests noch in weiter Ferne liegen.

Anders als bei automatisierten funktionalen Tests, muss das Monkey-Testing-System nicht an jede noch so kleine Änderung der fachlichen Anforderungen angepasst werden. Auch wenn der Test-Monkey vergleichsweise wenige Fehler findet, sind diese zu einem sehr frühen Auslieferungszeitpunkt bekannt und können zeitnah korrigiert werden. Zudem ermöglicht das zeitnahe Feedback, potentielle technische Risiken und Probleme schneller zu erkennen und frühzeitig gegenzusteuern.

Auch das Regressionsrisiko lässt sich durch Monkey-Testing deutlich reduzieren. Die Wunschvorstellung, alle funktionalen Tests zu automatisieren, um jederzeit alle bereits implementierten Features automatisiert testen zu können, scheitert oft an fehlenden Ressourcen im Testteam oder einer zu geringen Priorisierung der UI-Testautomatisierung.

Im agilen Projektumfeld müssen Teams zudem mit häufigen Änderungen der fachlichen Anforderungen umgehen. Ohne eine UI-Testautomatisierung, die dem aktuellen Stand der Anforderungen entspricht, lassen sich Regressionstests kaum in ausreichender Regelmäßigkeit und Breite durchführen. Der Versuch, die Lücken in der UI-Testautomatisierung durch mehr manuelle Regressionstests zu kompensieren, führt schnell dazu, dass die UI-Testautomatisierung noch mehr den Anschluss verliert. In der Folge werden Regressionstests oft unvollständig oder zu selten ausgeführt. Dabei reicht ein einfacher-, aber regelmäßig stattfindender (Anschalt-)Test aus, der sämtliche Formulare einer Web-Applikation aufruft und speichert. Auf diese Weise können Flüchtigkeitsfehler, wie diese beispielsweise aus Sourcecode-Merge- oder fehlerhaften Anwendungskonfiguration resultieren, aufgedeckt werden.

Wo liegen die Grenzen von Monkey-Testing?

Monkey-Testing liefert keinerlei Aussage darüber, ob die Anforderungen einer Applikation inhaltlich korrekt umgesetzt wurden. Hierfür müssen weiterhin funktionale Tests erstellt und manuell oder maschinell durchlaufen werden. Jedoch kann Monkey-Testing einen wertvollen Beitrag dazu liefern, die Robustheit einer Applikation gegenüber Fehlbedienung zu prüfen. Die Anzahl der explizit zu prüfenden Negativtests lässt sich damit in vielen Fällen reduzieren.

Durch Monkey-Testing lassen sich die funktionalen Tests der fachlichen Anforderungen NICHT ersetzen, können diese jedoch ergänzen. Einige Beispiele wie Anschalttests oder Reduzierung der Negativtestfälle wurden in diesem Artikel bereits genannt. Weitere Einsatzschwerpunkte sind Crashtest- oder Lasttest-Szenarien.

Hier einige Beispiele aus unserem Projektalltag:

Fehlende funktionale Tests im Scrum-Team
Nach dem zweiten Sprint ist das CI/CD System soweit etabliert, dass automatisierte Builds ausgeliefert werden können. Es fehlen jedoch noch funktionale Tests, um deren Qualität zu verifizieren.

Technische Migration auf ein anderes Datenbanksystem
Eine Web-Applikation wurde auf ein anderes Datenbanksystem migriert und auf der Testumgebung installiert. Die automatisierten Tests wurden mangels Zeit noch nicht auf das neue Datenbanksystem angepasst. Es ist unklar, ob das Deployment erfolgreich war und die Testumgebung für manuelle Tests freigegeben werden kann.

Fehlerhafter Sourcecode-Merge
Ein Feature-Branch wurde in den falschen Versionsstand des Trunks integriert. Der dafür verantwortliche Entwickler hat seinen Fehler mithilfe der automatisierten Unit-Tests rechtzeitig bemerkt und korrigiert. Dennoch ist unklar, ob alle Konfigurationsdateien in der richtigen Version eingecheckt wurden.

Integration neuer Features
Ein umfangreiches Feature wurde integriert, für das es noch keine automatisierten Tests gibt. Um das Testteam zu entlasten, soll der Release-Kandidat nur dann für einen manuellen Integrationstest freigegeben werden, wenn er ausreichend stabil ist.

Optimierung der funktionalen Testautomatisierung
Ein Release soll für den Abnahmetest auf der Umgebung des Kunden installiert werden. Aus Zeitgründen wurden nur Positivtestfälle automatisiert und der letzte manuelle Regressionstest liegt einige Wochen zurück. Da in der jüngeren Vergangenheit zufällig mehrere schwere Fehler entdeckt wurden, bestehen Zweifel an der Robustheit des Releases.

Für welchen Softwaretyp entsteht der größte Nutzen durch Monkey-Testing?

Den größten Nutzen erzielt Monkey-Testing, wenn Benutzeroberflächen ein standardisiertes Verhalten besitzen und einfach strukturiert sind.

Der Benutzer kann in einem Bereich der Benutzeroberfläche viele Aktionen ausführen, muss aber keine komplexen Navigationssequenzen durchlaufen, um andere Bereiche der Benutzeroberfläche zu erreichen. Hierzu gehört schwerpunktmäßig Software zur schematisierten Datenpflege (Objekte suchen, anlegen, bearbeiten, löschen).

Für komplexe Benutzeroberflächen mit vielen unterschiedlichen Navigationssequenzen, die je nach Datenkonstellation unterschiedliche Abzweigbedingungen ermöglichen, reduziert sich deutlich die Wahrscheinlichkeit, dass ein Test-Monkey die verschiedenen Abzweigbedingungen in endlicher Zeit durchlaufen kann.

Resümee

Wir nutzen in unseren Web-Projekten das Monkey-Testing, um Anschalttests mit den auslieferbaren neuen Software-Versionen durchzuführen. Verlaufen diese unauffällig, werden im Anschluss die teils sehr aufwendigen funktionalen Tests durchlaufen.

Bisher konnten wir nach Einführung der Test-Monkeys die Robustheit und somit die Qualität der ausgelieferten Software-Versionen – teils deutlich – steigern. Dabei geht die Qualitätssteigerung ohne manuellem Testaufwand einher.

Für uns hat sich Monkey-Testing als weiterer, fester Baustein der Testautomatisierung etabliert. Den geringen Einführungs- und Nutzungskosten steht ein schnelles und aussagekräftiges Feedback zur Qualität jeder neuen Software-Version gegenüber.

Antipattern im Bereich der Testautomatisierung – Der Belanglose

Der zweite Teil der Blogserie zum Thema Test-Antipattern beschäftigt sich mit überflüssigen Tests die ein Schattendasein in den Projekt-Repositories fristen. Sie interessieren sich für nichts was für den Projekterfolg von Relevanz ist und es interessiert sich auch niemand für sie – zumindest so lange sie nicht fehlschlagen oder angepasst werden müssen.

Viel Lärm um Nichts…

Beschreibung

Der Belanglose prüft eine Funktionalität die entweder bereits anderweitig getestet wurde oder die für den Projekterfolg irrelevant ist. Damit richtet er zunächst keinen Schaden an, bringt dem Projekt aber auch keinerlei Nutzen.

Unübersichtlich wird die Lage, wenn in einer Suite mit 300 Testfällen 30 deaktiviert sind und weitere 30 etwas testen, was mit dem aktuellen Stand der Requirements nicht viel zu tun hat. 30 deaktivierte Tests könnten gleichbedeutend sein mit 10% an nichtgetesteter Funktionalität, aber es könnte sich auch um Altlasten handeln die man bedenkenlos hätte löschen können.

Zusätzliche Verwirrung stiften die 30 aktiven aber fachlich eigentlich irrelevanten Testfälle. Laufen sie ohne Probleme durch erscheint das Testergebnis unangemessen positiv. Schlagen einige davon fehl, so wird eventuell Zeit damit verschwendet vermeintliche Defects zu analysieren und zu dokumentieren, die keine sind. Richtig teuer wird es, wenn eine größere Anzahl überflüssiger Testfälle weiterhin gewartet und angepasst werden muss.
Es gibt mehrere Indizien die darauf hinweisen, dass man einen belanglosen Test aufgespürt hat:

  1. Ein Test ist tautologisch ‚grün‘ oder seit längerer Zeit deaktiviert.
  2. Es sind keine oder nur wenige Assertions vorhanden die fast ausschließlich technische Aspekte oder Aufrufe von Third-Party Artefakten prüfen.
  3. Der Testcode wirkt unvollständig oder ist zu großen Teilen auskommentiert.
  4. Es gibt keine Kommentare die Rückschlüsse darauf zulassen welcher Testfall automatisiert wurde.
  5. Es gibt mehrere automatisierte Testfälle die laut Dokumentation exakt die gleiche Funktionalität prüfen.
  6. Häufige Änderungen der Requirements, sowie der Prioritäten der Entwicklungs- und Testaktivitäten deuten darauf hin, dass mit einer großen Anzahl obsoleter Testfälle gerechnet werden muss.

Ursachen

Es gibt verschiedene Ursachen die dazu führen können, dass Testfälle keinen für die Projektziele relevanten Nutzen erbringen:
Reibungsverluste in der Kommunikation und Zusammenarbeit innerhalb des Projektteams, wodurch Konzeption, Implementierung und Test aneinander vorbei arbeiten.

  1. Häufige Änderungen der Requirements und Umpriorisierungen in der Projektplanung, mit denen die Testautomatisierung nicht Schritt halten kann.
  2. Testfälle die zu feingranular geschnitten sind um eine fachlich sinnvolle Funktionalität abzubilden.
  3. Erstellung von Unit-Tests ohne fachlichen Bezug, die ausschließlich technische Aspekte oder das Verhalten externer Bibliotheken und Frameworks prüfen.
  4. Der Aufwand und Nutzen des automatisierten Testfalles werden nicht gegenübergestellt, d.h. wie hoch ist erwartete Nutzen, im Verhältnis des voraussichtlichen Automatisierungsaufwands.
  5. Unvollständig automatisierte Testfälle, „Copy & Paste“ von Testcode, sowie nachlässig durchgeführte Refactorings, wie das Aufteilen von Testfällen oder der Rückbau von nicht mehr benötigten Prüfungen.
  6. Als obsolet erkannte Testfälle die deaktiviert werden, anstatt sie zu löschen.

Maßnahmen

Wenn ein Test als belanglos enttarnt wurde, ist es in vielen Fällen kein großer Verlust ihn zu löschen. In anderen Fällen kann es notwendig sein, zumindest einen Teil der enthalten Prüfungen zu extrahieren und in andere Tests zu überführen oder den belanglosen Test zu vervollständigen.

Vorbeugung

Die Grundlage für die Testautomatisierung stellen die Testfälle dar, welche ausgehend von den funktionalen und nicht funktionalen Anforderungen formuliert wurden. Zusätzliche Unit-Tests die das interne Verhalten eines Bausteins testen, sollten diese ergänzen, jedoch nicht vollständig ignorieren oder ihnen gar widersprechen. Durch eine konsequente Umsetzung der Prinzipien von Test Driven Development (TDD) und eine enge Verzahnung von Requirements Engineering, Entwicklung und Test kann vermieden werden, dass sich Spezifikation, Applikationscode und Testfälle im Projektverlauf immer weiter voneinander entfernen.

Bei der Suche nach Testfällen für die Testautomatisierung stellt der voraussichtliche Automatisierungsaufwand nur eines von mehreren möglichen Auswahlkriterien dar. Viel entscheidender sind jedoch die fachliche Relevanz eines Themas und die Gegenüberstellung des voraussichtlichen Aufwands bei manueller und automatisierter Testausführung. Testfälle die einfach manuell getestet werden können oder nur sehr selten ausgeführt werden müssen, sind demnach nicht unbedingt bessere Automatisierungskandidaten als ein schwierig zu automatisierender Testfall.

Sind Testfälle zu feingranular geschnitten, damit ihre Automatisierung für das Projekt einen nennenswerten Mehrwert bringen kann, müssen sie ggf. zusammengeführt oder das Design der Testfälle überdacht werden.

Während des Refactorings sollte darauf geachtet werden, dass alle verbliebenen Artefakte im Hinblick auf die Testziele noch einen Nutzen bringen. Überflüssige Duplikate und belanglose Überreste von Testklassen sollten direkt entsorgt werden, da sie andernfalls womöglich unnötigen Aufwand generieren. Entwickler die Monate später Änderungen an diesen Altlasten vornehmen müssen, werden mit großer Wahrscheinlichkeit nicht mehr wissen, dass sie obsolet und die Mühen umsonst sind.

Werden Tests als obsolet erkannt, sollten diese gelöscht und nicht deaktiviert werden. Stellt sicher später heraus, dass ein gelöschter Test wieder benötigt wird, lässt sich dieser aus dem Projekt-Repository zumeist wieder rekonstruieren. Fraglich ist, ob der betreffende Testfall aufgrund der fortlaufenden Änderungsanforderungen überhaupt wiederverwendet werden kann oder besser neu geschrieben werden sollte.

Verwandte Antipattern

  1. Der Hochstapler
  2. Der Lügner
  3. Das Phantom

Fazit

Einzelne überflüssige Test-Karteileichen sind in einem nicht trivialen Projekt kaum zu vermeiden und richten keinen nennenswerten Schaden an. Wenn sich im Laufe der Zeit zu viel Ballast ansammelt, kann dieser das Projektteam zu einem denkbar ungünstigen Zeitpunkt überrollen. Wenn zum Beispiel nach einem umfangreichen Refactoring hunderte Tests nicht mehr compilierbar sind, bleibt keine Zeit um die Aufräumarbeiten durchzuführen die über Monate versäumt wurden. Daher sollte man die Anzahl der überflüssigen Tests stets im Blick behalten und proaktiv dagegen vorgehen, wenn diese überhand zu nehmen droht.

Antipattern im Bereich der Testautomatisierung – Einführung

Der Einsatz automatisierter Tests ist als Mittel der Qualitätssicherung aus Softwareprojekten nicht mehr wegzudenken. Viele Projektteams nutzen das Potential ihrer Testautomatisierung jedoch nicht vollständig oder machen sich das Leben unnötig schwer. Die Blog-Reihe stellt die in der Praxis am häufigsten beobachteten Unarten bei der Erstellung automatisierter Tests vor.

Einführung

Viele Wege führen bekanntlich nach Rom – vielleicht noch einige mehr zu einem korrekten, robusten, verständlichen und gut wartbaren automatisierten Testfall.

Während die meisten Projektteams bei der Entwicklung des Applikationscodes noch wohlüberlegt und methodisch durchdacht vorgehen, wird bei der Automatisierung der Testfälle oft munter drauflos programmiert. Ein zu später Einstieg in die Testaktivitäten, unzureichende Planung, ungeeignetes Design, fehlende Standards, lückenhafte Dokumentation und Mängel in der Qualitätssicherung der Testautomatisierung führen dazu dass sich Projektteams immer wieder hoffnungslos verrennen und nicht dort ankommen, wo sie ursprünglich hinwollten.

Die Blog-Reihe stellt einige der am häufigsten in Testautomatisierungsprojekten beobachteten Missstände als Liste von Antipattern vor.

Nach der Beschreibung des Sachverhaltes und der möglichen Ursachen, werden für jedes Antipattern Lösungswege sowie vorbeugende Maßnahmen aufgezeigt. Die Liste der Antipattern lässt sich gleichermaßen auf Unit-Tests, als auch auf automatisierte Integrationstests und auf UI-Tests anwenden.

  1. Der Belanglose – „Viel Lärm um Nichts
  2. Der Besserwisser – „Immer das letzte Wort haben“
  3. Der Chaot – „Der Weg ist das Ziel“
  4. Der Hochstapler – „The Show must go on“
  5. Der Lokalmatador – „My Host is my Castle”
  6. Der Lügner – „Wer einmal lügt dem glaubt man nicht“
  7. Der Optimist – „Wer durch die rosarote Brille schaut wird blind“
  8. Der Pedant – „So habe ich das schon immer gemacht“
  9. Das Phantom – „Wer war das nochmal“
  10. Die Schlafmütze – „Er läuft und läuft und läuft“
  11. Der Schmutzfink – „Nach mir die Sintflut“
  12. Der Schnüffler – „Es gibt Dinge die will man gar nicht wissen“
  13. Der Teamplayer – „Toll ein Anderer macht’s“
  14. Der Workaholic – „Bescheidenheit ist eine Zier“

Die noch nicht verlinkten Beiträge werden noch erstellt.