byteleaf logo
← zurück zum Blog

Einführung ins Behavior Driven Development

Behavior Driven Development macht es leicht, die Fachabteilung tiefer in die Spezifikation der Applikation mit einzubeziehen. Durch ein ausgeklügeltes Konzept ist es auch Nicht-Programmierern möglich, Spezifikationen in der Form von Testfällen zu schreiben und die Qualität der Software damit zu verbessern.
Simon Ittmann
Simon Ittmann
3. November 2020

Was ist eigentlich BDD?

Behavior Driven Development! In den letzten Jahren fällt dieser Begriff immer häufiger im Zusammenhang mit Test Driven Development (TDD), welches inzwischen sehr weit verbreitet ist. Manchmal spricht man auch von Specification Driven Development (SDD), das einfach nur ein Synonym für BDD ist. Doch was genau ist eigentlich BDD?

In der agilen Softwareentwicklung ist es üblich, dass der Fachbereich - in erster Linie Stakeholder & Product Owner - neue Features für eine Anwendung konzipieren, diese spezifizieren und in sogenannten User-Stories schriftlich festhalten. Diese Stories werden anschließend priorisiert an die Entwickler übergeben, um die Anforderungen letztendlich in die Anwendung integrieren zu lassen. Oft fehlt es den Anforderungen an essenziellen Details oder sie sind aus Sicht des Entwicklers nicht vollumfänglich durchdacht.

BDD ist der Versuch, den Fachbereich des Entwicklungsteams tiefer in den Entwicklungsprozess zu integrieren, um die Qualität der Spezifikationen (User-Stories) zu verbessern. Jedes Feature wird vom Fachbereich in mehrere Szenarien zerlegt und in einer einheitlichen Form schriftlich festgehalten:

1
2
3
4
5
6
7
Feature: Is it already Friday?
  Everybody wants to know if it's Friday

  Scenario: Sunday isn't Friday
    Given today is Sunday
    When I ask if it's already Friday
    Then I should be told "No"

Im Unterschied zum Schreiben einer User-Story, wird durch BDD vorausgesetzt, dass Beispiele und die erwarteten Ergebnisse genauestens spezifiziert werden.

Jede dieser Spezifikationen dient dem Entwickler später als Vorlage für einen Testcase. Analog zum Test-Driven-Development werden zunächst Tests implementiert, bevor mit der Anwendungsentwicklung begonnen wird. BDD ist damit als eine Erweiterung des klassischen TDD zu sehen.

Die Anwendung gilt als vollständig funktionsfähig und den Anforderungen der Stakeholder entsprechend, sobald alle Tests erfolgreich ausgeführt wurden. Die Stakeholder haben somit direkten Einfluss auf die Qualität der Software! Je besser die Testfälle definiert werden, desto stabiler wird später die Software sein.

Im Prinzip zeigt Behavior-Driven-Development wie man richtig testet – nämlich nicht die Implementierung, sondern das Verhalten des Codes aus fachlicher Sicht. Man spricht dabei von Blackbox-Testing. Klassisches Unittesting ist in der Regel Whitebox-Testing, man orientiert sich sehr stark am Sourcecode!

Ablauf

In der Praxis gibt es verschiedene Tools, die das Arbeiten mit BDD vereinfachen. Das Bekannteste ist wohl Cucumber und dessen Portierungen auf verschiedene Programmiersprachen. Hierbei wird die Gherkin-Syntax verwendet, um die Testfälle in einer formalen Sprache zu definieren.

Gherkin unterteilt Testfälle in Features, Szenarien und Schritte (Steps). Ein Feature entspricht dabei einer User Story und ist eine Sammlung von Szenarien. Ein Szenario ist ein konkreter Anwendungsfall, der das Verhalten der Software anhand einer Sequenz von Schritten beschreibt. Schritte sind unterteilt in Given-, When- und Then-Ausdrücke und werden in sogenannten Feature-Files festgehalten:

1
2
3
4
5
6
7
8
9
10
11
12
13
Feature: Shopping cart

  Scenario: Customer clears the shopping cart
    Given a customer puts a product into the shopping cart
    And the customer navigates to the cart detail page
    When he clicks on the "clear cart" button
    Then the cart should be empty

  Scenario: Test shopping cart detail page link
    Given a customer puts a product into the shopping cart
    And the customer navigates to the cart detail page
    When he clicks on the "product details" button of the product
    Then the application should navigate to the product detail page

Given beschreibt die Voraussetzungen des Tests. Das kann z.B. das Seeding von Datenbanktabellen oder das Setzen von Zuständen auf einer Website beinhalten. Die eigentliche Interaktion mit dem System findet im When-Schritt statt. Das Ergebnis dieser Interaktion wird in Then validiert.

Sobald die Feature-Files fertig definiert sind, können die Entwickler mit der technischen Umsetzung beginnen. Für jeden Ausdruck (Step) wird eine Funktion (Step-Definition) implementiert. Im nachfolgenden Beispiel sind Cypress End-to-End-Implementierungen zu sehen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';

Given('a customer puts a product into the shopping cart', () => {
  // code for adding a item into the cart ...
});

Given('the customer navigates to the cart detail page', () => {
  cy.visit('/shopping-cart');
});

When('he clicks on the "clear cart" button', () => {
  cy.get('[data-e2e=clear-cart]').click();
});

Then('the cart should be empty', () => {
  cy.get('[data-e2e=cart-items]').should('be.empty');
});

When('he clicks on the "product details" button of the product', () => {
  cy.get('[data-e2e=to-product-details]').click();
});

Then('the application should navigate to the product detail page', () => {
  cy.get('[data-e2e=headline]').contains('Product details')
});

Die Beschreibungen der Steps in den Feature-Files werden eins zu eins auf die zugehörigen Funktionen gemappt. Step-Definitions können mehrfach verwendet werden. Im Beispiel “Shopping Cart” werden die beiden Given-Implementierungen in beiden Szenarien verwendet. Eine Wiederverwendung von Step-Definitions kann langfristig eine Menge Test Code einsparen.

Die meisten IDEs bieten Autovervollständigung an, um bereits existierende Step-Definitions leichter zu finden und auf fehlende Steps hinzuweisen.

6d0d4a33-6fb0-4bf4-824a-e6c69f743198.jpeg

Wo machen BDD Tests Sinn?

Moderne Anwendungen sind meist so abstrakt und komplex, dass es wenig bis keinen Sinn macht, Kollegen und Kolleginnen des Fachbereichs in die Implementierungsdetails einzuarbeiten. Aus diesem Grund sollte vermieden werden, BDD für Unittests zu verwenden. In folgenden zwei Bereichen ist die Anwendung von BDD Tests sinnvoll:

  • API Tests: Wird die Schnittstelle ausschließlich vom eigenen Frontend genutzt, oder auch von Fremdapplikationen? Im ersten Fall spricht man von einem BFF (Backend for Frontend) und sollte sehr genau evaluieren, ob BDD Tests an dieser Stelle sinnvoll sind. Die API eines BFFs ist oft nicht sehr beständig. Das API-Design wird aufgrund der ausschließlichen internen Verwendung gerne vernachlässigt. Aus diesen Gründen macht es mehr Sinn sich mit dem BDD-Ansatz auf End-to-end-Tests zu fokussieren. Beim End-to-end Testing wird das interne BFF automatisch im vollen Umfang mit getestet. Wird die API von anderen Applikationen verwendet, ist sie damit das Endprodukt und BDD Tests sind essentiell. In diesem Fall muss der Fachbereich im API-Design geschult werden und ist daher auch in der Lage BDD Tests zu schreiben.

  • E2E (End-to-end) Tests: Verfügt die Anwendung über ein Frontend, ist die Verwendung des BDD Testings an dieser Stelle am sinnvollsten. In der Regel wird die Applikation vor jedem Release durch den Fachbereich über das Frontend getestet. Hierbei ist ein Leichtes, die manuellen Tests gleich in Feature-Files zu fassen.

Vorteile

In klassischen Softwareprojekten verlieren die Entwickler meist sehr viel Zeit, die genauen Spezifikationen während der Implementierung vom Fachbereich zu erfragen. Dieses hin und her ist zeitintensiv, anstrengend und kann schlimmsten Fall dazu führen, dass wichtige Testcases vergessen oder sogar falsch umgesetzt werden. Durch BDD wird versucht, hier Abhilfe zu schaffen und den Fachbereich zu einer klaren Spezifizierung aller Edge Cases zu motivieren. Diese Arbeitsweise kann, sofern sie konsequent durchgezogen wird, eine ganze Reihe von Vorteilen bringen:

  • Weniger Rücksprachen zwischen Entwicklern und Fachbereich - Spart Zeit, denn die Kommunikation ist oft der zeitintensivste Faktor in einem Projekt!
  • Entwickler müssen sich weniger mit der Fachlichkeit beschäftigen und haben mehr Zeit, sich auf die technische Umsetzung zu fokussieren - Verbesserung der Codequalität!
  • Konzeptionelle Probleme werden schon im Vorhinein erkannt - Geringeres Risiko die Entwicklung eines Features mitten in der Umsetzung aufgrund von konzeptionellen Fehlern oder schlecht durchdachten Workflows unter- oder gar abbrechen zu müssen.
  • Feature-Files sind gleichzeitig eine Art Code-Dokumentation - Diese können auch ohne Fachwissen leicht gelesen und verstanden werden.
  • Tools wie Cucumber sind in der Lage, Feature-Files auf die Test-Implementierungen zu mappen - Ein Given: create new customer kann Beispielsweise für mehrere Testfälle verwendet werden. Weniger Code durch intelligente Wiederverwendung möglich!

Probleme in der Praxis

In der Theorie liefert BDD nur Vorteile, wogegen es in der Praxis leider oft anders aussieht.

  • Feature-Files werden fehlerhaft angelegt: Beim Verfassen eines Feature-Files muss die Syntax der verwendeten Software penibel eingehalten werden. Die Feature-Files werden später auf die Code-Implementierungen gemappt und jeder noch so kleine Fehler kann die Tests zum Fehlschlagen bringen. Diesem Problem lässt sich mit professionellen Workshops entgegenwirken. Leider scheitert der Workshop-Ansatz oft am am Kosten- und Zeitinvest. Der in der Praxis gängige Ansatz ist es, die Feature-Files nochmals durch die Entwickler überprüfen und ausbessern zu lassen. Die Erfahrung zeigt, dass ein Entwickler ein Drittel seiner Zeit für die Implementierung und den Rest für das Korrigieren und Debugging der Feature-Files benötigt.
  • Koordination ist schwierig bei vielen Testfällen: Es ist nicht so einfach, bei einer großen Anzahl von Testfällen den Überblick zu behalten, vor allem beim Versuch den Test-Code wieder zu verwenden.
  • Feature-Files sind eine Code-Dokumentation, aber für wen? Feature-Files können als gute Dokumentation dienen, doch schaut man sich diese im Nachhinein jemals wieder an? Bugs werden in der Regel direkt einem Programmierer zugewiesen, welcher sich meist gleich dem Code zuwendet.
  • Verbessert sich die Qualität der Tests wirklich, wenn der Fachbereich eingebunden wird? Die Erfahrung unserer Entwickler zeigt leider oft, dass der Fachbereich die Feature-Files nicht ausreichend genau beschreibt. Die Qualität der Tests hängt letztendlich davon ab, ob der Entwickler Eigeninitiative zeigt und vergessene Testfälle nachzieht.
  • Hoher Pflegeaufwand: Wie bei allen anderen Arten von automatisierten Tests, müssen auch BDD Tests bei jeder Änderung des Codes angepasst werden. Das lässt sich leider nicht ändern...
  • Testumgebung aufsetzten ist sehr zeitintensiv: Gerade bei E2E- oder API-Tests muss in der Regel eine komplett neue Umgebung mit initial befüllter Datenbank hochgefahren werden. In Zeiten von Docker ist das keine unlösbare Aufgabe mehr, dennoch sollte man dafür einiges an Zeit einplanen!

Fazit

Projekte mit einem BDD Ansatz zu starten und zu versuchen, diesen konsequent durchzuziehen, ist eigentlich immer eine gute Entscheidung. Es ist auch kein Beinbruch, wenn man BDD nicht zu 100% umsetzen kann. In erster Linie geht es dabei nicht um die Entwickler, sondern darum, den Fachbereich zu animieren, schon vor dem Implementierungsbeginn detaillierte Gedanken über Spezifikationsdetails zu machen und diese in einheitlichem Format festzuhalten. Falls man dies auch nur ansatzweise hinbekommt, kann man den Entwicklungsprozess erheblich beschleunigen. Fachliche Designfehler der Anwendung werden frühzeitig erkannt, der Kommunikationsaufwand zwischen Fachbereich und Entwicklern wird erheblich reduziert und die Qualität der Testfälle wird verbessert. Damit wird eine stabilere Software gewährleistet.

Ein wichtiger Aspekt ist sicher auch, dass der Fachbereich ein besseres Gefühl dafür bekommt, wie viel Arbeit es wirklich ist, alle Spezifikationen bis ins kleinste Detail vorauszuplanen. Im Endeffekt ist Programmieren nichts anderes als fachliche Anforderungen in ein maschinenlesbares Format zu bringen.

Bei einer guten Spezifikationen können sich Entwickler ausschließlich auf die technische Umsetzung konzentrieren. Heutzutage ist es schwer genug, als Entwickler überhaupt auf dem neuesten Stand zu bleiben. Es sollte daher immer versucht werden, so viel Zeit wie möglich in die Implementierung zu stecken.

Wichtig ist ständig zu hinterfragen, ob der zusätzliche Aufwand von BDD in Relation zum Nutzen steht. Der Aufwand lohnt sich -meiner Erfahrung nach- nur bei größeren Projekten. Man sollte sich im Voraus Gedanken machen, ob man die richtige Teammentalität hat, um BDD auf lange Sicht durchführen zu können? Wenn der Fachbereich eigentlich gar kein Interesse an dem Thema zeigt, oder sich Programmierer bezüglich des zusätzliche Testaufwandes querstellen, sollte man es lieber lassen.

BDD funktioniert nur, wenn alle mitziehen!

Kontakt

E-Mail

info@byteleaf.de

Telefon

+49 89 90183650

Links

Code

GitHub

Wo wir sind

Adresse

byteleaf GmbH
Adamstraße 5
80636 München

ImpressumDatenschutzCookie-Einstellungen
© 2020 - Code: byteleaf - Design: Michael Motzek