Behavior Driven Development? – Wir wissen doch was wir tun!

Um ein Projekt erfolgreich umzusetzen, ist ein kontinuierlicher Wissensaustausch essentieller Bestandteil für ein funktionierendes Team. Damit erreicht man, dass alle Teammitglieder nahezu den selben Wissenstand haben. Doch je länger ein Team zusammensitzt, desto geringer wird die Wissensvielfalt. Man spielt sich aufeinander ein und es fehlt an neuen Impulse.

Aktuell bin ich Teil eines vierköpfigen Teams und Codequalität, sowie Tests spielen bei uns eine übergeordnete Rolle. Ein gegenseitiges Code Review mit Hilfe des Code Review Tools Gerrit gehört genauso zu unserem Prozess wie Stand-Up Meetings und Testabdeckung. Doch wenn man in festen Teams arbeitet läuft man Gefahr Dinge mit „ach, es wissen doch alle im Team wie es geht“ abzutun.

Genau diesen Fall hatten wir bei unseren Tests. Die Funktionalität stand im Vordergrund. Über die Lesbarkeit hat sich keiner sonderlich viele Gedanken gemacht. Es wusste ja jeder wie es geht. Dass das Team getauscht werden oder andere Personen zusätzlich hinzukommen könnten, daran dachte beim Erstellen keiner, schließlich hat man ja auch etwas Zeitdruck.

So kam es, dass heute Morgen Jonas neben mir stand und fragte, ob ich etwas Zeit für Pairing hab. Wir schauten also erstmal über den existierenden Code. Da wir regelmäßig diverse Refactoring Runden im Team machen, gab es an der eigentlichen Applikation wenig zu bemängeln. Die Tests wieder rum wurden einfach so mitgeschleift. Hauptsache sie laufen und testen das, was sie testen sollen. Dies sollte natürlich nicht der Anspruch sein. Wir machten uns also dran den Test umzubauen. Diese Pairing Runden sind gut, da man den Code selbst reflektieren muss und auch gleich etwas lernt. So wie ich in diesem Fall über Behavior Driven Development.

Der Test

Beispielhaft zeige ich hier einen Test vor und nach dem Refactoring.

Wir haben einen sehr simplen Calculator mit den Methoden divide(Double x, Double y) und getResult(). Testen wollen wir ob das Dividieren der ersten Zahl mit der zweiten Zahl zum korrekten Ergebnis führt. Wie wir alle aus der Schule wissen, kann man durch Null nicht dividieren. Daher bauen wir auch einen Test der diesen Fall prüft.

Unser Test nach dem Refactoring

Behavior Driven Development verfolgt das Ziel, dass die Funktionalität mit einfachen Sätzen beschrieben wird. In unserem Fall wären diesen Sätze wie folgt:

Gegeben ist eine Zahl 20 und eine weitere Zahl 5, wenn die erste Zahl mit der zweiten Zahl dividiert wird, dann sollte das Ergebnis 4 sein.

Gegeben ist eine Zahl 20 und eine weitere Zahl 0, wenn die erste Zahl mit der zweiten Zahl dividiert wird, dann sollte das Ergebnis Unendlich sein.

Unsere Tests mit Toolunterstützung

Natürlich gibt es auch für Behavior Driven Development Toolunterstützung. Tools wie JBehave oder JDave können in den Entwicklungsprozess integriert werden. Dort schreibt man die Stories in Textform und arbeitet mit den Annotations @Given, @When, @Then.

Zu guter Letzt hier noch der endgültige Code mit JBehave Integration. Zu erst müssen Stories definiert werden.

Eine kurze Erklärung zu den Schlüsselwörtern, die JBehave nutzt:

Narrative dient dazu die Userstory noch einmal zu beschreiben. Dieses Schlüsselwort wird aber von den Tests ignoriert und kann daher auch weggelassen werden.

Scenario(s) sind die einzelnen Testfälle. Diese werden mit einem kurzen Satz beschrieben.

Given, And, When und Then beschreiben den Testfall. Given beschreibt welche Voraussetzungen gegeben sein müssen. When sagt was ausgeführt werden muss. Dies muss zwingend ein Methodenaufruf sein. Then gibt an welches Ergebnis erwartet wird. Mit diesen Keywords erstellt JBehave aus den implementierten Schritten (siehe unten) einen Test.

Die Demo-Applikation ist auf Github zu finden. Wer mehr über Behavior Driven Development erfahren will kann hier schauen. Informationen zu JBehave gibt es auf der offiziellen Seite.

Das könnte Dich auch interessieren...

1 Antwort

  1. Jonas Kilian sagt:

    Meine persönlichen Regeln für BDD:

    1) Infinitest benutzen: http://infinitest.github.io/

    Hat nix mit BDD zu tun, aber ohne permanenten roten oder grünen Balken macht mir Testen keinen Spaß: „if the bar is green, the code is clean“ 😉

    2) keine Leerzeilen in Unit-Tests. Wenn ich eine Leerzeile brauche um die Übersichtlichkeit zu wahren, ist das ein Zeichen für zu hohe Komplexität, dann muss ich weiter am Abstraktionsgrad schrauben

    3)
    Test- Vorbedingungen (given) und Assertions (then) sind meist komplexer, weshalb ich sie zunächst in sprechende Hilfsmethoden auslagere, Bsp. „aTrialCustomer()“, „anExistingSessionCookie()“ etc.

    Wird das Setup immer komplexer, weil das Domänen- oder Komponenten-Modell eben komplex ist, dann nutze ich das Builder-Pattern (http://en.wikipedia.org/wiki/Builder_pattern) zusammen mit Method-Chaining (http://en.wikipedia.org/wiki/Method_chaining) um mir dynamisch flexible Objektstrukturen zusammen zu bauen, das sieht dann so aus:

    für’s Domain-Model:
    // given

    für Komponenten:
    // given

    Das mache ich sowohl für die Erstellung von Value-Objekten, Komponenten und Entitäten, als auch für die Assertions. Gerade bei Entitäten bietet diese Abstraktion den deutlichen Vorteil, dass man nicht zig Unit-Tests ändern muss, wenn sich das Datenmodell verändert. Bei allen Vorgehensweisen, bei denen Testdaten separat vom Code abgespeichert werden (wie z.B. DB-Unit), ist das nachträgliche Anpassen oft katastrophal bis schlicht unmöglich, weil man später nicht mehr weiß, warum ein Test dieses und jedes Daten-Szenario brauchte.

    4)
    Den eigentlichen Test-Aufruf (//when) werde ich nie mittels Hilfsmethode abstrahieren, damit man den Einsprung in den Live-Code sofort sehen kann:

    5)
    Negativ-Tests nicht über @throws abbilden, sondern so:

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Durch die weitere Nutzung der Seite stimmst du der Verwendung von Cookies zu. Weitere Informationen

Die Cookie-Einstellungen auf dieser Website sind auf "Cookies zulassen" eingestellt, um das beste Surferlebnis zu ermöglichen. Wenn du diese Website ohne Änderung der Cookie-Einstellungen verwendest oder auf "Akzeptieren" klickst, erklärst du sich damit einverstanden.

Schließen