Language Engineering

Akzeptanztests spielend erstellen: Der Test-Editor macht's möglich

Akzeptanztests dienen im Softwareentwicklungsprozess dazu, sicherzustellen, dass die umgesetzte Lösung die fachlichen Anforderungen wie gewünscht abdeckt. Solche Tests stellen damit einen essentiellen Baustein zur Qualitätssicherung und Projektsteuerung dar. Dieser Blogbeitrag beschreibt einerseits die Herausforderungen beim Erstellen von Akzeptanztests, andererseits erfährst du, wie der quelloffene „Test-Editor“ eine Vielzahl dieser Herausforderungen löst.

Besonders wertvoll sind Akzeptanztests dann, wenn sie, ganz im Sinne des „Test-First-Prinzips“, direkt zusammen mit den Anforderungen spezifiziert werden und somit bereits während der Entwicklung zur Verfügung stehen.

In agilen Entwicklungsprozessen komplementieren Akzeptanztests die User-Stories und stellen sicher, dass die formulierten Akzeptanzkriterien überprüfbar sind.

Automatisch ausführbare Akzeptanztests können bereits während der Entwicklung als Richtschnur und Fortschrittskontrolle dienen. Dies reduziert Iterationen zur Nachbesserung und verteilt Ressourcen möglichst optimal.

Häufig ergibt sich bei der Erstellung von Akzeptanztests allerdings ein Dilemma: Auf der einen Seite verfügen nur die Anwender über das domänenspezifische Fachwissen, um die Tests den Anforderungen entsprechend zu formulieren. Auf der anderen Seite werden zur Automatisierung der Tests aber Programmierkenntnisse benötigt, so dass diese Aufgabe den Softwareentwicklern zufällt.

Wird die Testautomatisierung von Entwicklern vorgenommen, birgt dies die Gefahr, dass sich bei der Implementierung von Testspezifikationen Fehler einschleichen, etwa durch missverständliche Formulierungen in den Testspezifikationen oder durch unzureichendes Verständnis der Fachdomäne.

In der Praxis wird deshalb auf die Automatisierung ganz oder teilweise verzichtet.

Die Alternative zu automatisierten Tests ist die manuelle Durchführung von Akzeptanztests. Sie ist jedoch fehleranfällig, nicht zuverlässig wiederholbar und um ein Vielfaches aufwändiger, sodass sie nicht prozessbegleitend eingesetzt werden kann.

Da manuelles Testen für die iterativ-inkrementelle Entwicklung ungeeignet ist, beraubt es den Entwicklungsprozess der Agilität. Schlimmstenfalls werden Akzeptanztests als so aufwändig eingeschätzt, dass gänzlich auf sie verzichtet wird.

Eine solche Lücke in der Qualitätssicherung führt zu einem entsprechend erhöhten Risiko für das Auftreten von Fehlern im Produktivsystem, verbunden mit hohen Kosten für Nachbesserungen und Wartung.

Gängige Rollenverteilung in Akzeptanztestwerkzeugen
Abbildung 1: Gängige Rollenverteilung in Akzeptanztestwerkzeugen

Kern des Problems ist, dass in der Akzeptanztesterstellung die Belange von Fachanwendern und Entwicklern nicht entlang ihrer jeweiligen Kenntnisse und Fähigkeiten getrennt werden. Werkzeuge wie etwa Cucumber oder Gauge stellen im Wesentlichen die Nachverfolgbarkeit zwischen Testspezifikationen und ausführbaren Testfällen her.

Erstere werden durch Fachanwender in natürlicher Sprache verfasst, Letztere durch Entwickler unter Bezug auf die jeweiligen Spezifikationen implementiert. Abbildung 1 zeigt diese Aufgabenverteilung schematisch mit Beispielausschnitten aus einem Cucumber-Tutorial.

Wie wäre es aber, wenn wir Fachanwender selbst in die Lage versetzen könnten, automatisch ausführbare Tests zu beschreiben?

Da Fachanwender in der Regel nicht über Programmierkenntnisse verfügen, muss ein geeignetes Werkzeug Konzepte zur Testbeschreibung anbieten, die entweder allgemeinverständlich sind, sich also zum Beispiel an natürlicher Sprache orientieren, oder sich aus dem fachspezifischen Vokabular der Anwendungsdomäne ableiten.

Einen solchen Ansatz verfolgt der Test-Editor, den ich im Folgenden vorstelle.

Der Test-Editor

Mit Hilfe des webbasierten Test-Editors können Fachanwender ohne IT-Kenntnisse Nutzerakzeptanztests spezifizieren. Der Test-Editor generiert daraus Testfälle, die sich im Zuge eines Continuous Build automatisiert ausführen lassen.

Das Alleinstellungsmerkmal des Test-Editors ist dabei, dass die Testfälle nicht in einer Programmiersprache wie Java beschrieben werden. Stattdessen kommen natürliche Sprachausdrücke zum Einsatz. Testschritte werden dadurch allgemeinverständlich definiert.

Rollenverteilung im Test-Editor
Abbildung 2: Rollenverteilung im Test-Editor

Analog zu Abbildung 1 stellt Abbildung 2 den Ansatz des Test-Editors dar, der eine klare Trennung der Belange von Fachanwendern, Testern und Entwicklern vornimmt.

Die technischen Aspekte, die die Testfallbeschreibung an die Anwendung binden und Programmierkenntnisse erfordern, sind als Anwendungsmappings herausgetrennt worden. Dies ermöglicht es, die Testfälle in einer Weise zu formulieren, die von Fachanwendern nachvollzogen werden kann: Sie werden in die Lage versetzt, selbst die Rolle des Testers auszufüllen.

Anwendungsmappings definieren das Interaktionsmodell der zu testenden Anwendung und damit die zur Verfügung stehenden Testschritte. Sie müssen einmal pro Anwendung – nicht einmal pro Testfall – beschrieben werden. Außerdem können generische, anwendungsübergreifende Teile herausgetrennt werden. Im Vergleich zum „herkömmlichen“ Modell, bei dem Entwickler die Tests schreiben, ist dies eine deutliche Aufwandsreduzierung.

Die Automatisierungslogik für die Testausführung wird in Test-Fixtures gekapselt. Dies sind Java-Bibliotheken, die das Testen bestimmter Anwendungstypen ermöglichen. Eine Web-Fixture zum Testen von Webanwendungen ist bereits vorhanden. Eine Angular-Fixture baut darauf auf. Mit der Host-Fixture lassen sich IBM-3270-Terminal-Anwendungen testen, und für Swing-Anwendungen gibt es eine Java-Swing-Fixture. Eine Erweiterung ist problemlos möglich.

Die folgenden Abschnitte stellen kurz die Benutzeroberfläche des Test-Editors vor (Abschnitt 2.1) und zeigen anhand von Beispielen, wie damit Testspezifikationen (Abschnitt 2.2) und Testfälle (Abschnitt 2.3) erstellt und bearbeitet werden, wie Tests ausgeführt werden und wie die Ergebnisse analysiert werden können (Abschnitt 2.4).

Da das Erstellen von Anwendungsmappings nicht Teil des eigentlichen Testprozesses ist, sondern eher als Teil der Anwendungsentwicklung gesehen werden sollte, wird dies in diesem Beitrag nicht weiter betrachtet. Einen guten Eindruck von Anwendungsmappings vermittelt ein entsprechendes Tutorial auf den offiziellen Test-Editor-Webseiten.

Die Test-Editor-Benutzeroberfläche

Abbildung 3 zeigt die webbasierte Benutzeroberfläche des Test-Editors. In der Mitte befindet sich der Editierbereich, in dem Dateien zur Bearbeitung geöffnet werden können. Um diesen Bereich herum befinden sich ein Datei-Browser (links), eine Übersicht der zur Verfügung stehenden Testschritte (rechts), sowie die Testausführungssicht (unten) mit einer baumartigen Testschrittübersicht und einer Detailansicht daneben.

Die Oberfläche ist bewusst einfach gehalten, um Anwendern nur die Bedienelemente anzubieten, die sie für ihre Aufgabe wirklich benötigen. Dies unterscheidet den Test-Editor von Werkzeugen, die beispielsweise auf Basis einer Rich-Client-Plattform aufgebaut wurden. Derartige Tools erben häufig Features, die nicht benötigt werden und die Nutzer irritieren.

Die Benutzeroberfläche des Test-Editors
Abbildung 3: Die Benutzeroberfläche des Test-Editors

Da Anwender den Test-Editor in unterschiedlichen Rollen nutzen, gibt es weitere Möglichkeiten, nicht benötigte Elemente auszublenden; so können alle genannten Teilbereiche in der Größe verändert, aber auch vollständig an den Fensterrand minimiert werden.

Es ist folglich möglich, nur den Editor mit der Testschrittübersicht oder nur die Testausführungssicht anzuzeigen. Der Dateibrowser zeigt Dateien farbkodiert an:

  • grün für Testspezifikationen
  • blau für Testfälle und verwandte Dateien
  • rot für Anwendungsmappings

Dies entspricht den drei wesentlichen Nutzerrollen Fachanwender, Tester und Entwickler. Über entsprechende Schaltflächen am unteren Rand des Dateibrowsers können Filter aktiviert werden, um diese Dateiarten zu selektieren oder auszublenden, und so den Fokus auf die gerade zu verrichtende Aufgabe zu setzen.

Testspezifikationen

Testspezifikationen geben vor, was ein Benutzer von einer Anwendung erwartet. Wird agil, etwa nach SCRUM, vorgegangen, finden sie sich als Akzeptanzkriterien der User-Stories wieder. Alle Fachanwender sollen Testspezifikationen formulieren können, weshalb der Test-Editor nahezu keine Einschränkungen oder Vorgaben bezüglich der Struktur von Testspezifikationen macht.

Lediglich eine Unterteilung in Spezifikationsschritte ist vorgesehen, die jeweils durch ein Sternchen eingeleitet werden – die Testschritte selbst können frei in Prosa formuliert werden. Auf dieser Ebene unterscheidet sich der Test-Editor also noch kaum von anderen Akzeptanztestwerkzeugen.

Beispiel für eine Testspezifikation "CreateHero.tsl"
Abbildung 4: Beispiel für eine Testspezifikation

Abbildung 4 zeigt ein Beispiel für eine Testspezifikation. Die zu testende Anwendung stammt aus dem „Angular Tour of Heroes“-Tutorial und stellt eine Liste fiktiver Superhelden dar, die um weitere Einträge ergänzt werden kann. Die abgebildete Testspezifikation fordert, dass, wenn auf der Übersichtsseite ein Held hinzugefügt wird, dieser anschließend in der Liste erscheint. Das hier verwendete Given-When-Then-Schema ist lediglich eine Konvention, die verwendet werden kann, aber nicht muss.

Die Testspezifikation wird als Datei mit der Endung *.tsl (Test-Specification-Language) abgelegt. Sie dient im nächsten Schritt dem Tester als Vorlage zur Konkretisierung eines Testfall.

Testfälle

Testfallbeschreibungen bilden den wahrscheinlich wesentlichsten Baustein des Test-Editors und stellen gleichzeitig das Hauptunterscheidungsmerkmal im Vergleich mit anderen Werkzeugen dar.

Testfälle können für sich alleine stehen, beziehen sich im Regelfall aber auf eine zuvor erstellte Testspezifikation, die sie durch Angabe konkreter Ablaufschritte konkretisieren. Die Sprache, in der Testfälle formuliert werden, muss zwei wesentliche Anforderungen erfüllen: zum einen soll sie von Fachanwendern nachvollzogen und angewendet werden können, zum anderen muss sie ausführbar sein, damit die Tests automatisch ablaufen können.

Der Test-Editor löst dies, indem er Testern Testschritte anbietet, die in natürlicher Sprache formuliert sind, jedoch über genügend Struktur verfügen, um automatisch in JUnit-Tests übersetzt werden zu können.

Damit Tester sich über diese Strukturvorgaben möglichst wenig Gedanken machen müssen, bietet ihnen der Test-Editor eine Reihe von Funktionen, die sie bei der Testfallerstellung unterstützen:

  • An jeder Stelle des Testfalldokuments kann die Autovervollständigung aktiviert werden, die eine Liste der im jeweiligen Kontext gültigen nächsten Textbausteine zur Auswahl anbietet. Tester müssen daher weder die genauen Strukturvorgaben noch die verfügbaren Testschritte auswendig kennen.
  • Eingaben im Test-Editor durchlaufen eine ständige Validierung, sodass Fehleingaben unmittelbar im Editor durch eine Markierung an der entsprechenden Stelle hervorgehoben werden. Dazu gehört eine Fehlermeldung, die erklärt, was das Problem ist und wie der Fehler korrigiert werden kann.
  • Syntaxhervorhebung betont die Struktur der Testfälle. Dies geschieht unter anderem durch unterschiedliche Farben, was die Testfälle leichter lesbar und Fehler besser erkennbar macht.
  • Die Testschrittübersicht am rechten Bildschirmrand ergänzt den Editor um eine hierarchisch sortierte Liste derjenigen Testschritte, die in Testfälle hineinkopiert werden können. Dies ermöglicht einen Gesamtüberblick und erleichtert neuen Testern den Einstieg in den Test-Editor.
Beispiel für die Testfallerstellung im Test-Editor
Abbildung 5: Beispiel für die Testfallerstellung

Abbildung 5 greift das vorherige Beispiel wieder auf und zeigt einen entstehenden Testfall, der die zuvor beschriebene Spezifikation implementiert.

Die Spezifikationsschritte tauchen hier wieder auf, werden aber jeweils durch Testschritte konkretisiert. Die Testschritte sind jeweils Komponenten zugeordnet: Der erste Spezifikationsschritt benötigt die anwendungsunabhängige Komponente Webbrowser, die Testschritte zum Starten eines Browsers und zum Navigieren zur URL der zu testenden Anwendung bietet.

Zumeist modellieren Komponenten aber Teile der zu testenden Anwendung, wie hier die Heroes-Komponente, die die Heldenübersichtsseite repräsentiert. Sie stellt Testschritte zur Verfügung, die Interaktionen mit auf dieser Seite verfügbaren Steuerelementen ermöglichen, beispielsweise das Klicken auf den Add-Button und das Eingeben eines Heldennamens in das dafür vorgesehene Textfeld.

In der Animation ist vor allem die oben beschriebene Autovervollständigung gut zu sehen. Wesentliche Teile des Testfalls werden gar nicht vom Tester vollständig eingetippt, sondern als fertige Versatzstücke eingefügt und gegebenenfalls angepasst.

Zu sehen ist auch, dass Elemente, mit denen interagiert werden soll, in spitze Klammern eingefasst sind. Eingabewerte erscheinen in Anführungszeichen. Die Autovervollständigung erübrigt nicht nur, diese Zeichen zu tippen, oder sich zu merken, welche Zeichen jeweils gefordert sind, sondern hilft auch, gültige Werte innerhalb von in Anführungszeichen oder spitze Klammern gefasste Bereiche auszuwählen.

Beispielsweise können für die beiden Klick-Testschritte nur die Schaltflächen ausgewählt werden, die auf der Heroes-Seite tatsächlich existieren. Ein Vertippen bei manuell eingegebenen Schaltflächennamen bemerkt die Validierung sofort und markiert den Fehler am Zeilenrand.

Der vollständige Testfall für dieses Beispiel findet sich am Ende des entsprechenden Tutorials auf der offiziellen Test-Editor-Website.

Die Testfallsprache des Test-Editors bietet eine Reihe weiterer Features, die in diesem einfachen Beispiel nicht zur Anwendung kommen. Sie sind sehr hilfreich, wenn es darum geht, komplexere Testfälle zu strukturieren und große Mengen von Tests zu organisieren. So können zum Beispiel Setup- und Teardown-Blöcke definiert und als sogenannte Test-Frames ausgelagert werden.

Zur Wiederverwendung von Testschrittabfolgen können Makros definiert werden. Außerdem können Testfälle parametrisiert werden, um zum Beispiel den gleichen Ablauf mit unterschiedlichen Eingabedaten testen zu können.

Testausführung

Die im Test-Editor erstellten Testfälle können dort direkt ausgeführt werden. Im unteren Bereich der Benutzeroberfläche befindet sich die Testausführungssicht (siehe Abbildung 6).

Wird ein Testfall ausgewählt, erscheint hier eine Baumdarstellung, die den Test hierarchisch in Spezifikations- und Testschritte zerlegt, gegebenenfalls auch noch weiter, etwa wenn Makros verwendet wurden. Ein Klick auf die Start-Schaltfläche führt den Test im Hintergrund auf einem Server aus. Sobald Teilergebnisse verfügbar werden, wird dies im Baum durch farbige Symbole markiert: grün für bereits erfolgreich abgeschlossene Schritte, rot für den Fall, dass Fehler aufgetreten oder Testannahmen nicht wie erwartet eingetreten sind.

Zu jedem Testschritt können in der Detailansicht Eigenschaften wie Status (erfolgreich oder nicht), Ausführungsdauer und konkrete Variablenwerte eingesehen werden. Bei graphischen Oberflächentests wird außerdem ein Screenshot pro Testschritt angefertigt. Anhand dieser Screenshots kann der Testablauf verfolgt werden.

Die Test-Logs können ebenfalls eingesehen und nach drei Detailstufen gefiltert werden. Dabei richtet sich die gröbste Stufe eher an Fachanwender und enthält dementsprechend auch nur fachliche Informationen und gegebenenfalls Fehlerbeschreibungen. Tester und Entwickler können eher technische Ablauf- und Fehlermeldungen zuschalten, um Fehlerursachen im Detail nachvollziehen zu können.

Testausführung im Test-Editor
Abbildung 6: Testausführung im Test-Editor

Testfälle lassen sich aber auch ohne weitere Anpassungen unabhängig vom Test-Editor ausführen. Dazu bindet man sie in den Buildprozess der zu testenden Anwendung ein: Ein Gradle-Plugin sorgt für die Übersetzung der Testfälle nach Java.

Das Ergebnis sind JUnit-Tests, die als Teil der Test-Suite der Anwendung ausgeführt werden können. Insbesondere ist so eine nahtlose Einbindung der Akzeptanztests in Continuous-Integration-, Delivery- und Deployment-Prozesse möglich.

Fazit

Akzeptanztests sind von zentraler Bedeutung für die Qualitätssicherung in der Softwareentwicklung. Im Unterschied zu vielen anderen Werkzeugen versetzt der Test-Editor die Fachanwender, die auch die Anforderungen stellen, selbst in die Lage, ihre Spezifikationen durch automatisch ausführbare Tests zu konkretisieren.

Aufwändiges und fehleranfälliges manuelles Testen gehört damit der Vergangenheit an. Zudem wird es möglich, nach einem Test-First-Ansatz vorzugehen, sodass die Tests den gesamten Entwicklungsprozess begleiten und leiten können.

Der Test-Editor ist weder auf eine Fachdomäne noch auf bestimmte Anwendungstechnologien beschränkt. Testschritte können im Fachvokabular der Anwendungsdomäne definiert werden. Neben Webanwendungen lassen sich beispielsweise Java-Swing- oder Host-Anwendungen testen – Erweiterungen um zusätzliche Technologien sind problemlos möglich.

Zu den Vorteilen des Test-Editors gehört auch, dass er als Webanwendung nicht lokal installiert werden muss, sondern im Browser des Nutzers „lebt“. Dadurch ist er im Vergleich zu schwergewichtigen Desktop-Anwendungen zugänglicher und leichter bereitzustellen und zu warten.

Als containerbasierte Anwendung fügt sich der Test-Editor gut in moderne Anwendungslandschaften ein; zudem hilft die Technologie, vollständig isolierte und damit wiederholbare Testabläufe sicherzustellen.

Der Test-Editor ist eine Open-Source-Anwendung und wird aktiv weiterentwickelt. Weitere Informationen und Dokumentation findest du auf der offiziellen Website, der Quellcode ist auf GitHub verfügbar.

Was hältst du von Akzeptanztests und welche Herausforderungen hast oder hattest du bei diesem Thema? Schreib es mir in die Kommentare!

   
Über Jan Jelschen

Jan ist seit 2017 als Software- und Language-Engineer bei itemis in Hamburg tätig und mit der Weiterentwicklung des Test-Editors betraut. Zuvor war er mehrere Jahre in der universitären Forschung und Lehre im Bereich Softwaretechnik tätig. Zu seinen Forschungsschwerpunkten und Interessen gehören unter anderem Softwarequalität und -migration, Softwarearchitektur und -integration, sowie modellgetriebene Softwareentwicklung und domänenspezifische Sprachen.