Immer wieder einmal taucht der Begriff Behavior Driven Development oder BDD auf. Obwohl das verhaltensgetrieben Entwickeln einige interessante Vorzüge fällt der Einstieg oftmals schwer. Dabei basiert BDD auf den Techniken im Test Driven Development. Das größte Problem scheint eine vor allem das Fehlen einer für Einsteiger verständliche Einführung in BDD zu sein. Im Folgenden werden daher einige Grundprinzipien des BDD zusammengetragen, die den Einstieg erleichtern sollen.
Test Driven Development (TDD) ist eine Entwicklungsmethode die seit einigen Jahren immer mehr Verbreitung findet. Dabei geht es hauptsächlich darum, Tests vor der eigentlichen Implementierung zu entwerfen und zu schreiben. Eine Möglichkeit stellen hierzu Unit-Tests dar, die i.d.R. auf die kleinste zu testende Einheit (Unit) angewendet werden. Da das Konzept prinzipiell vielversprechend ist, haben sich Unit-Tests auch in der Clean-Code-Developer-Bewegung eingefunden. Das bekannteste Werkzeug zum Erstellen von Unit-Tests in der .NET-Welt ist derzeit NUnit, das ursprünglich von JUnit portiert und für die .NET-Plattform angepasst wurde. Ein noch immer gutes Buch für den Einstieg in die NUnit ist Pragmatic Unit Testing in C# with NUnit von Andy Hunt und David Thomas.
Während in der Theorie alles wunderbar klingt, hat sich bei zahlreichen Gesprächen mit Kollegen und Freunden immer wieder eine Reihe von Frage gestellt.
Wo fange ich überhaupt an zu testen? Was soll ich jetzt testen? Wie soll das ich testen? Wann höre ich endlich auf zu testen?
Im Bestreben möglichst viele Tests zu schreiben und eine 100%ige Code-Abdeckung zu erreichen schleichen sich jedoch schnell Trivial-Tests (z.B. für einfache Properties) ein, denn diese sind einfach und schnell zu schreiben, schlagen nie (oder zumindest selten) fehl und erwecken den Anschein, dass der Code aufs Ausführlichste getestet sei.
Ein alternativer Ansatz, der zumindest einige der Fragen beantwortet, ist das Behavior Driven Development (BDD). Ein guter Einstiegspunkt ist der Artikel Introducing BDD von Dan North. Als Ausgangspunkt dient hierbei die Beschreibung einer User-Story.
As a [ROLE]
I want [GOAL]
so that [MOTIVATION]
Im Laufe des Artikels wird die Verhaltensbeschreibung der Implementierung weiter in ein Given-When-Then-Muster ausgebaut.
Given some initial context (the givens),
When an event occurs,
then ensure some outcomes.
Bei BDD ist durchaus die richtige Wortwahl für entscheidend. Anders als beim reinen TDD stellt sich jedoch nicht die Frage, was getestet werden soll, sondern wie sich das System verhalten soll. Verwendet man Dan Norths Einstiegspunkt As-I-So, kann man das Verhalten aus Nutzersicht beschreiben, und dadurch Test auf Basis von User-Storys schreiben.
Konzentriert man sich darauf, nur die für den Test benötigte Funktionalität zu implementieren hilft das unter anderem dazu den Fokus nicht zu verlieren und Feature Creep zu vermeiden. Code der nicht benötigt wird um das Verhalten zu erreichen ist vermutlich nicht notwendig, egal wie viel Spaß es vermutlich macht ihn zu schreiben. In diesem Sinne unterstützt BDD das YAGNI-Prinzip. Im Artikel Getting started with BDD style Context/Specification base naming von Jean-Paul Boodhoo finden sich einige Spezifikationen, die einen ersten Eindruck geben, wie diese aufgebaut sein könnten.
Ein wenig verwirrend ist die Tatsache, dass nur wenig klare Beschreibungen bezüglich des Vorgehens bei BDD sind. Ein Blick beispielsweise in den Code Magazine-Artikel Behavior-Driven Development zeit einige der Grundkonzepte von BDD auf.
Eine gemeinsame Sprache (Shared Language/Ubiquitous Language) ist die Grundvoraussetzung zur Erstellung von Spezifikationen (Specifications) beispielsweise nach dem As-I-So Ansatz. Das Ergebnis hiervon ist eine Reihe von Akzeptanzkriterien die zur Abnahme der gewünschten Funktionalität zu erfüllen sind. Dabei könnte die Granularität der Spezifikation wie folgt verfeinert werden: User Story (Akzeptanzkriterium) –> Kontextspezifikation –> Code (Ausführbare Spezifikation). Die Kontextspezifikation (Context Specification) besteht aus dem Tripel Concern – Context – Obersvation.
Die Beobachtung (Observation) beschreibt was der eigentliche Test überhaupt macht bzw. was überhaupt getestet wird. Die Organisation von Tests sollte dabei nach dem Concern erfolgen, wobei mehrere Beobachtungen pro Belange (Concern) möglich sind. Im dotnetpro Artikel Die Gleiche Sprache Sprechen von Stefan Lieser (Ausgabe 11/2009) findet sich ein recht gutes Beispiel, dass dies verdeutlichet. Der Kontext (Context) beschreibt Systemzustände (oder Voraussetzungen), die mehreren Beobachtungen gleich sind.
Im Gegensatz zu TDD sollte die Benennung von Tests keine technischen Details kommunizieren sondern das gewünschte Verhalten auf Basis der gemeinsamen Sprache (Shared Language) wiederspiegeln. D.h. nach dem Hinzufügen des ersten Kunden in eine Kundenliste anstelle der Test-Methode IsNotNullTest könnte in BDD eine Beobachtung ContainsOneCustomer lauten.
Der Mechanismus dahinter (gleichgültige welche Werkzeuge verwendet werden) ist immer noch TDD, allerdings wird nicht mehr die kleineste Funktionseinheit getestet sondern (im optimalen Fall) ein in sich abgeschlossenes Teil-Verhalten des Systems. Natürlich schließt der Einsatz von BDD der Einsatz von TDD nicht aus.
Grundsätzlich kann jedes TDD-Framework für BDD genutzt werden. Dariusz zeigt diesem Artikel wie BDD mittels MSTest aussehen könnte. Vorteil ist ganz klar, dass alles innerhalb der Visual Studio-IDE stattfindet, die Code-Abdeckung berechnet werden kann und die Tests mit nur wenigen Handgriffen ausgeführt werden können. Reine TDD-Frameworks werden jedoch das Problem auf, das sich die Sprache der Spezifikation nicht ohne weiteres ändern lässt.
Eine Alternativen sind z.B. die auf xUnit.net basierenden xUnit.BDDExtensions von Björn Rochel, die auch von Alex Zeitler in Behavior Driven Development (BDD) leicht gemacht mit ReSharper und XUnit.NET verwendet werden.