Maximilian Wallisch
Behavior Driven Development – oder kurz BDD – ist eine von Dan North entwickelte Technik, mit der bereits beim Erstellen der User-Stories das Verhalten der Applikation konkret und menschenlesbar beschrieben werden soll. Dies dient mehreren Zwecken, birgt aber auch ein paar Herausforderungen und scheint in der Theorie weit simpler als es in der Praxis ist. Da ich in einigen Projekten Erfahrungen mit BDD sammeln konnte, möchte ich anhand eines konkreten Beispiels einen kleinen Einblick in eine von vielen möglichen Varianten geben, BDD in der agilen Softwareentwicklung einzubetten.
Projekthintergrund
In dem Projekt, welches ich für meine Erläuterungen heranziehen möchte, war ich als Test-Manager tätig. Es wurde eine Modernisierung eines Webportals unter Berücksichtigung von Ansätzen aus der Service-orientierten Architektur durchgeführt. Es wurden also verschiedene, gekapselte Services entwickelt, welche dann in der gemeinsamen Funktionalität als komplette Module für fachliche Prozesse genutzt werden.
Wir setzten für den Test dieser Services sehr stark auf Praktiken, die aus dem BDD-Ansatz stammen. Die Implementierung dieser Praktiken entsprach aber nicht exakt dem von Dan North definierten Vorgehen, eher war es eine Reduktion auf einige wesentliche, als Projektteam gut einsetzbare Methoden.
Die im Folgenden geschilderten Erfahrungen daraus sind daher nicht als Referenz für ein lupenreines BDD-Vorgehen zu betrachten, sondern spiegeln meine persönlichen Projekteindrücke zu diesem Thema wider.
Warum BDD?
Für die Umsetzung der angeforderten Module wurde ein agiles/iteratives Modell mit einem 2-wöchigen Rhythmus gewählt. Es entstanden daher regelmäßig Stories, wobei eine Story im Normalfall ein Service beschrieben hat.
In Verwendung mit diesen Stories setzten wir Methoden aus BDD ein, um folgende Ziele zu erreichen:
- Veranschaulichung des in der Story beschriebenen Applikationsverhalten durch konkrete Beispiele
- Schaffen eines einheitlichen Verständnisses unter allen Beteiligten
- Abdeckung der Akzeptanzkriterien mit BDD-Szenarien
- Schaffen einer Grundlage für Testautomatisierung
Im Zuge der Umsetzung haben sich einige Prinzipien und Vorgehensweisen herauskristallisiert, welche sich auch über mehrere Sprints als nützlich erwiesen haben. Da eine ausführliche Schilderung des konkreten Vorgehens den Rahmen dieses Blogartikels übersteigen würde, möchte ich mich stattdessen auf einige Schlüsselfaktoren konzentrieren und diese kurz beschreiben. Ich freue mich aber über Kommentare oder konkrete Fragen zu einzelnen Punkten.
Mehraugenprinzip
Die für BDD-Szenarien vorgegebene Syntax „Given, When, Then“ führt dazu, sich explizit über das Verhalten und den Nutzen bzw. die Ausgabe einer Funktion Gedanken zu machen. Wenn sich bei den Story-Refinement Sessions verschiedene Rollen (z.B. Tester, Business Analyst, Softwarearchitekt, Entwickler, Product Owner) mit den BDD-Szenarien auseinandersetzen wird das gemeinsame Verständnis von allen gefördert. Gleichzeitig steigt die Sicherheit, dass das in der Story beschriebene Service auch wirklich das tut was es soll und einen Nutzen hat.
Ein Thema, viele Perspektiven - das ist es, was zählt!
Verfügbarkeit von Testdaten
Als Test-Manager oder Tester ist es wichtig, bei der Erstellung der Szenarien die benötigten Testdaten zu berücksichtigen und nach Möglichkeit am besten gleich anonymisierte Echtdaten oder Daten aus dem Testsystem zu definieren.
In weiterer Folge hilft das auch dabei, die aus den BDD-Szenarien erstellten automatisierten Tests schneller in die Testumgebung zu integrieren und dort lauffähig zu halten. Ein Test der sich zum Beispiel auf eine Ortsangabe bezieht wird mit einem Datensatz wie „Musterstraße 7, Musterstadt 1234“ in einer simulierten Umgebung, die mit Mockups arbeitet, funktionieren - jedoch kaum auf einer Testumgebung, die mit echten Daten aus dem Produktivsystem arbeitet.
Wird der Fachbereich bzw. End User in die Erstellung der Szenarien miteinbezogen, erhält man oft wertvollen Input über Sonderfälle oder spezielle Datenkonstellationen, die mit den Tests abgedeckt werden können.
Beim Definieren der benötigten Testdaten werden somit auch oft Lücken in der Anforderung bzw. in der Beschreibung der Story oder den Akzeptanzkriterien aufgedeckt - beispielsweise beim Verhalten im Umgang mit verschiedenen Datenformaten.
Szenarien kategorisieren
Es ist nützlich, die Szenarien gleich bei der Erstellung mit einem Flag zu kategorisieren, damit auf einen Blick ersichtlich ist um welche Art von Szenario es sich handelt (z.B. @Regression, @Performance, @System-Integration, @Smoketest, usw.).
Das hilft dabei bestimmte Tests schneller zu finden oder kann dazu verwendet werden alle Tests aus einer Kategorie zusammenzufassen und gemeinsam auszuführen. Nach einem Deployment sollten beispielsweise alle Tests der Kategorie „Smoketest“ erfolgreich sein bevor mit weiterführenden Tests begonnen wird.
Vorbedingungen klein halten
Eine der wichtigsten Erkenntnisse in diesem Projekt war, darauf zu achten, dass die Szenario-Outline (welche den Umfang des Szenarios beschreibt) bzw. die „Given“-Blöcke nicht zu groß werden, da es sonst schnell unübersichtlich und zu komplex wird.
Sollten die Vorbedingungen zu umfangreich werden, ist es oft sinnvoller die Szenarien feingranularer zu beschreiben, da sich so auch in den meisten Fällen die Vorbedingungen reduzieren und die Komplexität überschaubar bleibt.
Zu große bzw. zu komplexe Szenarien können aber auch ein Zeichen dafür sein, dass die Story zu umfangreich ist oder zu wenig detailliert beschrieben ist. Als Daumenregel hat sich hier bewährt bei mehr als 8-10 Akzeptanzkriterien die Story aufzuteilen.
Zu komplex? Der Schlüssel ist, sich auf das Verhalten zu konzentrieren!
Komplexität beherrschen
Bei Software mit hohem integrativem Anteil (viele Anbindungen an Drittsysteme) werden die Szenarien zumindest in der Ausführung schnell sehr komplex, da auf die vielen verschiedenen Drittsysteme Rücksicht genommen werden muss. Besonders wenn die benötigten oder verwendeten Daten durch mehrere Systeme manipuliert werden, bevor sie für den eigentlichen Test als Input verwendet werden können oder als Output einer Funktion überprüft werden.
Um hier potenzielle Gefahren zu vermeiden, empfiehlt sich ebenfalls das Story-Splitting.
Eine wichtige Erkenntnis war aber auch das Fokussieren auf die Beschreibung des Verhaltens der Applikation, die es zu Testen bzw. zu Entwickeln gilt. Oft beschrieben unsere Szenarien auch das Verhalten von Drittsystemen, obwohl nur der Input oder Output von diesen Systemen relevant war.
Bleiben die Szenarien trotzdem komplex, sorgt eine gemeinsame Sicht auf die Dinge wenigstens für Verständnis bei allen Beteiligten.
Komplexität birgt auch ein Risiko für die Automatisierung, da es viele Abhängigkeiten gibt und die Tests dadurch nur schwer isoliert durchführbar sind. Hier kann man auf Mocking oder Service-Virtualisierung zurückgreifen, läuft jedoch eventuell Gefahr, sich zu sehr auf diese Lösung zu verlassen und Fehler die mit dem Datenhandling zusammenhängen erst in späteren Teststufen zu finden.
Notation einheitlich gestalten
Die Notation von BDD ist prinzipiell vorgegeben, jedoch gibt es für verschiedene Konstellationen innerhalb des Szenarios trotzdem verschiedene Varianten um diese niederzuschreiben. Zum Beispiel kann ein Szenario wie folgt beschrieben sein:
Scenario: Edit Size in ‘Choose Size’ UI
Given I am on the „Choose Size“ tab in the UI
And the Size is 0
When I click the „Edit“ Button
And I click in the Input field
And I enter the value „50“
And I click the save Button
Then the new Size should be 50
Diese Beschreibung ist sehr detailliert und bezieht sich sehr stark auf UI Elemente (Button, Input field) und das Vorgehen. Sie ist daher sehr wartungsintensiv – wenn sich zum Beispiel der Button ändert oder sich das generelle Vorgehen nicht mehr im Tab „Choose Size“ sondern an einer anderen Stelle der Applikation befindet.
Besser ist zum Beispiel folgende Notation:
Scenario: User wants to edit Size
Given the Size is <initial>
When the User changes the Size to <newSize>
Then the new Size should be <ResultSize>
Example:
Initial | newSize | ResultSize |
0 | 50 | 50 |
50 | 20 | 20 |
20 | 0 | 0 |
In diesem Fall ist egal wie die Buttons benannt sind und an welcher Stelle in der Applikation diese Aktion durchgeführt werden kann. Hier zählt nur das Verhalten, welches in drei Ausprägungen beschrieben ist – die restlichen Details sollten in der Story beschrieben sein.
Im Projekt hat es ein paar Sprints gedauert, bis alle an den Stories beteiligten Kollegen abstrakt genug gedacht haben, um sich wirklich nur auf das Verhalten zu konzentrieren und nicht auf Implementierungsdetails.
Wichtig ist in jedem Fall sich auf ein gemeinsames Vorgehen zu einigen und dieses auch laufend zu erweitern und die Erkenntnisse darüber festzuhalten.
Verantwortlichkeiten definieren
Gerade bei verteilten Teams, wie wir es in diesem Projekt waren, ist es wichtig sich im Klaren darüber zu sein, welche Rolle wofür zuständig ist. Hier bieten die BDD-Szenarien einen guten Ansatzpunkt, um sich für die verschiedenen Rollen Aufgaben abzuleiten.
Der Tester übernimmt z.B. das Bereitstellen von Testdaten; der Entwickler/Test-Automatisierer das Umsetzen des Tests im Code; der Product Owner die Absprache mit dem Fachbereich bei unklaren Szenarien und der Architekt die Einbettung nicht beschriebener Funktionalität in einem neuen Service.
Fazit
BDD einzusetzen kann zu Beginn etwas herausfordernd sein, aber in diesem Projekt hat es sich bewährt, da wir auch immer wieder unser Vorgehen hinterfragt und angepasst haben. Gerade die Klarheit über die Verantwortungen hat bei uns für sehr viel Verständnis für die Arbeit und die Aufgaben der Kollegen gesorgt. Sie hilft auch jedem Einzelnen dabei ein bisschen über den Tellerrand hinaus zu blicken. Genau das ist für mich auch der größte Vorteil des beschriebenen Vorgehens – BDD fördert und fordert die Kommunikation und Abstimmung im Team und hilft dadurch bereits frühzeitig ein hohes Qualitätsniveau im gesamten Projekt zu erreichen.
Agile, Behaviour-Driven Development, Test and Development, Software-Entwicklung
Maximilian Wallisch
Agile, Behaviour-Driven Development, Test and Development, Software-Entwicklung