Überstunden: Go oder No-Go?

Insbesondere mit dem Scrum-Commitment gibt es immer wieder Diskussionen über die ungeliebten Überstunden. Muss das Team Überstunden schieben, um sein Commitment zu halten? Diese Diskussion will ich hier aber gar nicht führen – vielleicht später. Bei der Diskussion um Überstunden habe ich häufig das Gefühl, dass es sich in der agilen Welt um eine heilige Kuh handelt. Überstunden sind ein No-Go und fertig. Tatsächlich glaube ich, dass wir uns mit dieser Haltung einer Option berauben, die man in der passenden Situation durchaus in Betracht ziehen sollte.

Und genau zu so einer Situation habe ich ein Praxisbeispiel. Wir haben vor einiger Zeit ein Projekt angenommen, in dem wir eine Webanwendung für eine Versicherung entwickeln sollten. Für das Projekt hatten wir 2,5 Monate Zeit und die Deadline war ebenso hart wie sportlich. Und da der Termin so eng war, haben wir gemeinsam im Team beschlossen, dass jeder von Beginn an soviele Überstunden macht, wie er selbst verantworten kann. Wir haben alle eine ganze Weile Überstunden geschoben. Die Menge hat aber über die Zeit abgenommen, weil wir immer sicherer wurden, dass wir den Termin halten können. Letztlich haben wir den Termin auch gehalten und es wurde eines der produktivsten und befriedigsten Projekte, an dem ich bisher beteiligt war – und das bei wirklich konsequentem TDD, sehr hoher Testabdeckung und sehr gut strukturiertem Code.
Bei der Retrospektive haben alle Teammitglieder die Überstunden-Maßnahme begrüßt. Sie wurde in einer „Jo, wir schaffen das“-Atmosphäre geboren (im Gegensatz zur sonst üblichen „Wir arbeiten soviel wir können, damit wir hinterher keinen Anschiss kriegen“-Atmosphäre). Es war also „Play to win“.

Zur Klarstellung: Ich rede hier nicht von Überstunden, die irgend jemand anordnet. Ich rede davon, dass das Team Überstunden bewusst und gewollt über einen kurzen und verantwortbaren Zeitraum einsetzt.

13 Leute, 5 Firmen, 3 Standorte. Unmöglich! Oder?

„Unser Team besteht aus 13 Leuten, die aus 5 Unternehmen stammen und an drei Standorten (inkl. Offshore) arbeiten. Die Teammitglieder sind deutlich erkennbar spezialisert: Neben Java-Entwicklern gibt es Web-Entwickler, Leute für den Content und Qualitätssicherer. Wir haben nicht einen PO, sondern zwei. Und ein Teil der Leute ist auch nur Teilzeit verfügbar. Ach ja: Wir haben übrigens das Product Backlog direkt vor dem Sprint nochmal ordentlich umgekrempelt.“
Wenn man mich vor drei Wochen gefragt hätte, wie groß ich die Erfolgswahrscheinlichkeit für dieses Team einschätzen würde, hätte ich wahrscheinlich „sehr niedrig“ gesagt. Und das ist wahrscheinlich auch die korrekte Antwort. Aber „sehr niedrig“ ist eben doch etwas mehr als 0, sprich: Das Team hat wirklich sehr gut als Team gearbeitet und ein sehr schönes Ergebnis produziert.

Sehen wir uns dazu einmal ein paar Eckdaten an:

  • Es wurde nach Scrum gearbeitet.
  • Es gab zwei Product Owner, aber es war klar, wer von den beiden im Zeifel die Hosen anhat.
  • Je Standort gab es Colocation inkl. der Product Owner.
  • Der Sprint hatte ein klares, ziemlich wichtiges Ziel: Das System geht für einen definierten Anwenderkreis live.
  • In der Sprint-Planung wurde ein Task-Breakdown durchgeführt. Die Tasks wurden nicht in Stunden geschätzt. Stattdessen gab es die Regel, dass ein Task maximal einen Tag dauern darf und dann wurden die Tasks einfach gezählt.
  • Der ScrumMaster hat bei der Sprint-Planung und während des Sprints dafür gesorgt, dass in Bezug auf dieses Ziel Minimalismus galt. Es wurden nur die Stories aufgenommen, die unbedingt für das Sprintziel notwendig waren.
  • Die Anforderungen wurden als sehr kleine User-Stories formuliert. Noch während der Sprint-Planung wurden die User-Stories weiter in kleinere User-Stories aufgeteilt, so dass die User-Stories danach nur jeweils einen Umfang von wenigen Tagen hatten.
  • Es gab jeden Tag zwei Daily Scrums. Zuerst eines über Skype mit den Standorten und danach noch eines am Hauptstandort.
  • Der ScrumMaster hat insbesondere beim Daily Skype Scrum dafür gesorgt, dass Probleme auch sichtbar und bearbeitet wurden.
  • Am Hauptstandort stand auch das Taskboards und die Sprint-Burndowns (geführt nach Anzahl Tasks und nach Anzahl Story Points).
  • Die Burndowns wurden von Hand auf Flipchart-großem Papier erstellt und gepflegt. Die Stories waren natürlich auch elektronisch vorhanden, aber das elektronisch generierte Burndown war nie wirklich korrekt – die Ursachen dafür zu erklären, führt hier zu weit. Auf jeden Fall waren die händisch erstellten Burndowns die führenden Burndowns.
  • Es gab einen ständig aktiven Skype-Chat für alle Team-Mitglieder.
  • Das Sprint-Review nach dem Sprint war öffentlich im Unternehmen. Die Anzahl der zusätzlichen Teilnehmer war sehr überschaubar. Da aber der zuständige Fachbereichsleiter auch anwesend war, wurde die große Bedeutung des Sprints explizit. Außerdem war damit klar, dass man im Sprint-Review nicht mit einem zufälligen Herumgeclicke durch die Tür kommt.
  • Das Team hat es genau geschafft, das Sprintziel zu erreichen und das System termingerecht live zu stellen. Das Team ist sehr stolz auf seine Leistung und die Product Owner sind sehr zufrieden mit dem Erreichten.

    Damit hier kein falscher Eindruck entsteht. Das war kein einfacher Sprint, den man mal so runterarbeitet. Es gab einen ganz Zoo von Problemen während des Sprints. Die Qualitätssicherer waren zunächst nicht so verfügbar, wie geplant, so dass dort ein Bottleneck entstand. Die Build-Infrastruktur ist bezogen auf automatisierte UI-Akzeptanztests faktisch zusammengebrochen (und während des Sprints auch nicht wieder auferstanden), etc. Aber das Team hat pragmatische und gangbare Lösungen gefunden. Und die bestanden nicht einfach darin, Qualität zu opfern. In dem Projekt wurden sehr viele automatisierte Akzeptanztests geschrieben und die entstandenen Code- und Teststrukturen können sich sehen lassen.

    Meine ganze persönliche Bewertung der Geschichte: Ich glaube, dass Fokussierung ausschlaggebend für den Erfolg des Teams war. Dazu hat es z.B. sehr geholfen, nur die Stories einzuplanen, die unbedingt für das Sprintziel notwendig waren. Das Team hätte sich während des Sprints zusätzliche Stories geben lassen, wenn es schneller gewesen wäre als geplant. Dazu ist es aber nur in ganz kleinem Umfang gekommen. Stattdessen gab es auch keine „Versuchung“ schnell noch ein paar optionale Stories umzusetzen. Stattdessen wurde mehr Wert auf Qualität und Tests gelegt.

    Und einen wesentlichen Anteil an der Fokussierung hatte der ScrumMaster. Das Projekt ist ein schönes Beispiel dafür, wieviel Unterschied ein guter ScrumMaster machen kann.

    P.S.: Ich schreibe das hier nicht, um mich selbst zu loben. Ich habe den ScrumMaster nicht ausgebildet und für das Team kein Scrum-Coaching gemacht – ich war für ein anderes Thema als Coach im Team und bezogen auf Scrum hier nur staunender Beobachter.

XP-Days Germany am 26-28.11.09 in Karlsruhe

XP Days Germany ist die größte deutschsprachige Konferenz zur agilen Softwareentwicklung. Sie findet dieses Jahr vom 26. bis 28. November in Karlsruhe statt.

Einige Highlights:

  • Keynote von Alistair Cockburn
  • Zwei Halbtagestutorials mit begrenzter Teilnehmerzahl
  • Vier parallele Tracks am Hauptkonferenztag
  • Mehrere Pecha-Kucha-Blöcke
  • Community Day mit Open Space und World Cafe

Das detaillierte Programm,den Link zur Anmeldung und zahlreiche weitere Informationen gibt es auf http://www.xpdays.de

Laufende Neuigkeiten über Teilnehmerzahlen, freie Plätze, Programmänderungen etc. werden über Twitter verbreitet. Informationen dazu gibt es hier: http://xpdays.de/2009/twitter.html

Ich selbst werde mit einem Vortrag zum inkrementellen Entwurf und einem Pecha-Kucha-Vortrag zu Stop-the-Line vertreten sein.

Ich hoffe, wir sehen uns auf den XP-Days.

Skriptsprachen: Was ich bisher übersehen hatte…

Die Verfechter von Ruby, Python etc. sprechen von dynamischen Skriptsprachen. Ich war bisher immer der Meinung, dass der Hauptunterschied zu Sprachen wie C++ und Java im Typsystem besteht. Java und Konsorten sind statisch getypt. Ruby, Python etc. sind je nach Sprechweise dynamisch getypt oder ungetypt.
Daher war ich der Meinung, man müsste eher ungetypt/dynamisch getypt gegen statisch getypt stellen. Und dann wären Sprachen wie Smalltalk oder Lisp eben auch in der Gruppe der ungetypten/dynamisch getypten Sprachen.

Neulich habe ich aber gemerkt, dass der Aspekt der Skriptsprache doch wichtiger ist, als ich angenommen hatte. Ich musste auf meinem Rechner einen Proxy-Server installieren, um bestimmte URLs umzulenken. Und dazu musste man ein Skript (z.B. in Perl) schreiben, das die URLs in die Ziel-URLs umwandelte.

Ich habe mir dazu ein Perl-Skript als Vorlage aus dem Internet besorgt. Mit meinen beschränkten Perl-Kenntnissen habe ich das Skript nicht wirklich verstanden, aber das schien mir auch nicht notwendig.

Das hat natürlich erstmal alles nicht funktioniert. Es wurden keine URLs umgelenkt. Und natürlich war mir auch unklar, wo das Problem liegt. War die Konfiguration des Proxy-Servers falsch oder stimmte etwas mit dem Perl-Skript nicht?

Das ließ sich aber ganz leicht herausfinden. Einfach das Perl-Skript mal so starten.
./redirect.pl
An der Fehlermeldung konnte man sofort sehen, dass das Skript Daten über die Pipe erwartet. Das ergibt auch Sinn: Der Proxy-Server gibt die aktuelle URL per Pipe an das Perl-Skript. Auch diese Situation kann ich direkt auf der Linux-Shell nachstellen:
echo http://meineUrl.de | ./redirect.pl
Und siehe da, das Skript läuft durch, erzeugt aber nicht die Ausgaben, die ich erwartet habe. Folgerung: Mit dem Skript stimmt etwas nicht. Mit Quellcode-Anstarren und etwas Internet-Recherche habe ich das Problem dann auch schnell identifiziert und das Skript korrigiert.

Stellen wir uns zum Vergleich vor, das Redirect-Skript wäre in Java programmiert gewesen (C++ stellen wir uns hier lieber nicht vor…). Dann hätte ich das Programm nicht einfach so aufrufen können. Ich hätte erstmal den Classpath zusammenstellen müssen. Oder ich hätte mir einen Unittest geschrieben, um das Skript zu testen. Und für das Austesten direkt zusammen mit dem Proxy-Server hätte ich immer wieder neu compilieren müssen. Auf jeden Fall wäre das ganze ein gutes Stück schwergewichtiger mit deutlich längeren Turn-Around-Zeiten gewesen.

Und dieser Vorteil zeigt sich nicht nur bei so kleinen Hilfsskripten. Auch in echten Anwendungen ist es nützlich, wenn man einen verdächtigen Teil seiner Anwendung einfach so aus der Shell starten kann.

Hochproduktive Teams kochen

Joseph Peline schreibt an einem Buch über hochproduktive, selbstorganisierte Teams. Ein paar Draft-Auszüge hat er in seinem Blog veröffentlicht, die bereits sehr interessant aussehen.

Joseph verwendet Kochen als Metapher. Zu wenig Hitze und das Essen wird nicht gar, zuviel Hitze und das Essen verbrennt. Und dieses Modell wendet Jospeph auf Teams an. Sie sollen „kochen“. Das ist der Zustand, in dem das Team wirklich hochproduktiv arbeitet, Gerade weil die Teammitglieder ihre Komfortzone verlassen müssen.

die Herausforderung besteht jetzt also darin, die Teams mit dem richtigen Maß an Hitze zu versorgen. Ich hatte zuerst Schwierigkeiten mit dem Modell, weil ich unterbewusst Hitze mit Druck gleichgesetzt habe. Aber so ist es sicher nicht gemeint – jedenfalls nicht Druck im klassischen Sinne („Ihr müsst genau diese Funktionen, zu genau dieser Zeit zu genau diese Deadline schaffen, sonst….“). Mit Hitze ist hier das gemeint, was notwendig ist, um die Teammitglieder zu aktivieren, dafür zu sorgen, dass sie ihre Komfortzone verlassen.

So betrachtet hilft das Modell, Phänomene zu verstehen, die mir immer wieder über den Weg laufen. Da gibt es z.B. Scrum-Teams, die jedes Commitment halten und auch der Product-Owner ist zufrieden. Trotzdem fühlt es sich so an, als wäre irgendetwas nicht richtig. Man spürt keine Energie. Nach Josephs Koch-Metapher sind wir auf Level 3: Cooling (Stagnation). Das Team erledigt seine Arbeit verlässlich und routinisiert, aber es arbeitet letztlich deutlich unter seinen Möglichkeiten und zumindest mir würde die Arbeit in so einem Team auch keinen Spaß machen.

Ich bin wirklich sehr gespannt, wie sich Joseph’s Buch weiterentwickelt und hoffe, dass es bald komplett veröffentlicht wird.

Hier sind die bisher veröffentlichten Ausschnitte:

  1. Turning up the heat – introduction
  2. Turning up the heat – the basic model
  3. Turning up the heat – the levels

BDD for Prolog: How To

ProSpec is a RSpec (http://rspec.info) inspired BDD framework for Prolog. I created it during my experiments with Prolog. I suspect that I did some strange things. I would be pleased by any feedback on how to improve ProSpec or the examples. Send me an E-Mail: stefan AT stefanroock DOT de

Every ProSpec specification starts with describe(FixtureName) and ends with end_describe. Within these two predicates every specification is declared with it(SpecName/SpecCode).

We start with a very simple BDD spec for bowling.

bowling_spec.pro:

:- ensure_loaded('ProSpec.pro').
:- ensure_loaded('bowling.pro').

:- describe('Bowling'). 

generate_bowling_hits(_, 0).
generate_bowling_hits(Hit, Count) :- bowling_hit(Hit), Count2 is Count-1, generate_bowling_hits(Hit, Count2).

:- it('should score 0 for gutter game'/(
	generate_bowling_hits(0, 20),
	bowling_score(Score),
	assert_that(Score, equals:0)
)). 

:- end_describe.

bowling.pro:

bowling_hit(Count).
bowling_score(0).

With „run_specs.“ the BDD spec runs:

Bowling...
...should score 0 for gutter game (passed)
Passed:1

1 specs passed

In the next step with specify the next behaviour increment:

bowling_spec.pro:

:- ensure_loaded('ProSpec.pro').
:- ensure_loaded('bowling.pro').

:- describe('Bowling'). 

generate_bowling_hits(_, 0).
generate_bowling_hits(Hit, Count) :- bowling_hit(Hit), Count2 is Count - 1, generate_bowling_hits(Hit, Count2).

:- it('should score 0 for gutter game'/(
	generate_bowling_hits(0, 20),
	bowling_score(Score),
	assert_that(Score, equals:0)
)). 

:- it('should sum hits of a game'/(
	generate_bowling_hits(2, 20),
	bowling_score(Score),
	assert_that(Score, equals:40)
)). 

:- end_describe.

„run_specs.“ shows that this spec fails:

Bowling...
...should score 0 for gutter game (passed)
...should sum hits of a game
	Expected 40 equal to 0

We extend the bowling implementation:

bowling_hit(Count) :- assert(bowling_hit_def(Count)).

bowling_score(Score) :- 
	findall(Hit, bowling_hit_def(Hit), Hits),
	sumlist(Hits, Score).

Now the spec succeeds, but only once. The reason is the growing number
of „bowling_hit_def“ facts. To avoid this problem ProSpec offers „setup_spec“.

bowling_spec.pro:

:- ensure_loaded('ProSpec.pro').
:- ensure_loaded('bowling.pro').

:- describe('Bowling'). 

generate_bowling_hits(_, 0).
generate_bowling_hits(Hit, Count) :- bowling_hit(Hit), Count2 is Count - 1, generate_bowling_hits(Hit, Count2).

:- dynamic(setup_spec/0).

setup_spec :- 
	retractall(bowling_hit_def(_)).

:- it('should score 0 for gutter game'/(
	generate_bowling_hits(0, 20),
	bowling_score(Score),
	assert_that(Score, equals:0)
)). 

:- it('should sum hits of a game'/(
	generate_bowling_hits(2, 20),
	bowling_score(Score),
	assert_that(Score, equals:40)
)). 

:- end_describe.

Now the spec succeeds every time.

There is a bunch of other assert_that predicates in ProSpec.pro. Have a look
and proceed: http://github.com/stefanroock/ProSpec/

Reflektionen über Prolog

Ich erkläre meine Experimente mit Prolog jetzt erstmal für beendet und schließe mit ein paar Reflektionen über Prolog. Es ist durchaus möglich, dass ich an der einen oder anderen Stelle einem Irrtum aufgesessen bin. In diesem Fall freue ich mich über entsprechende Kommentare.

  1. Prolog als Sprache fand ich ganz lustig, auch weil die Ansätze so anders sind als das, was man sonst so in Programmiersprachen vorfindet. In diesem Sinne war die Beschäftigung mit Prolog auf jeden Fall zur Horizonterweiterung nützlich.
  2. Für Prolog gibt es einen ISO-Standard, so dass Prolog-Systeme verschiedener Hersteller kompatibel sein sollten. Das ist eigentlich ganz nett. Aber je mehr Programmiersprachen mit ISO-Standard ich kennenlerne, umso mehr habe ich den Eindruck, dass eine ISO-Standardisierung die Fahrkarte ins Reich der toten Programmiersprachen ist.
  3. Möglicherweise ist das auch der Grund dafür, warum es keine Weiterentwicklung von Prolog zu geben scheint. Das finde ich persönlich schade, weil ich die Ansätze vielversprechend finde. Aber irgendwie müsste man die Konzepte noch in die heutige Zeit bringen.
  4. Und ein Nutzen wäre da. Soweit ich das überblicken kann, macht eine Rule-Engine ungefähr, das was Prolog macht – nur eben in einer proprietären Sprache. Wenn man Pech hat, kommt auch noch XML ins Spiel die ganze Geschichte wird auch noch schwer lesbar. Wäre es nicht viel smarter, Prolog dafür zu verwenden?
  5. Prolog hat den Anspruch, den Entwickler weitestgehend von Algorithmen zu entlasten und stattdessen nur noch Spezifikationen schreiben zu lassen. Das finde ich einen lohnenswerten Ansatz. Ich hatte das Gefühl, dass ich tatsächlich weniger Algorithmik betreiben muss. Ich muss aber im Gegenzug wissen, wie Prolog die Regeln auswertet – also letztlich welche Algorithmen Prolog anwendet. Und das fand ich nicht immer trivial. Daher hatte ich auch so meine Schwierigkeiten, den Cut-Operator sinnvoll einzusetzen. Aber vielleicht wird das einfacher, mit mehr Übung?
  6. In die gleiche Problemkategorie fällt, dass ich zwischendurch immer mal Situationen hatte, wo eine Regelauswertung auf einmal 20 Sekunden gedauert hat – bei einer handvoll Fakten. Und mir war nicht immer klar, wie das passiert ist. Insgesamt sollen aber Prolog-Programme auch nicht langsamer sein als Programme, die in anderen Programmiersprachen entwickelt werden.
  7. Beeindruckend fand ich, dass ich mein Terminplaner-Beispiel in Prolog mit extrem wenigen Zeilen Code schreiben konnte, ohne dass der Code schwer verständlich wurde. Allerdings hat das Beispiel wieder Benutzungsoberfläche noch Datenbank. Wenn beides dazukommt und man die Lösung mit Rails oder Grails vergleicht, ist der Unterschied vermutlich nicht mehr so drastisch. Schließlich ist SQL ja sowas wie Prolog-Light.
  8. Apropos Benutzungsoberfläche und Datenbank: Prolog ist keine isolierte Regel-Auswertungsmaschine. Anbindungen an Rich-Client-Libs, HTTP und Datenbanken existiert in Form von Libraries.
  9. Durch sein Pattern-Matching ist es mit Prolog ganz einfach, eine internen DSL zu formulieren – auch wenn man sich wünschen würde, die Klammern der Root-Regel noch loswerden zu können.
  10. Regeln haben in Prolog keinen Rückgabewert. Stattdessen werden als Variablen angegebene Parameter zu diesem Zweck verwendet. Das in sehr flexibel und funktioniert eben auch für mehrere Rückgabewerte. Auf der Negativseite steht, dass man für einfache Ausdrücke eigene Variablen einführen muss. Möchte man an eine Regel index+1 übergeben, muss man dazu eine Extra-Variable einführen. Das finde ich definitiv umständlich.
  11. Die meisten Prolog-Systeme bieten ein Modulkonzept. Das ist nicht im ISO-Standard enthalten, aber zwischen die Prolog-Systemen aber sehr ähnlich. Ich habe das Modulsystem nicht benutzt, aber es sieht vernünftig aus.
  12. Außerdem gibt es sowas wie Objektorientierung (mind. in SWI-Prolog), aber das sieht echt merkwürdig aus.
  13. Und dann ist da noch die Datenbasis mit den Regeln und Fakten. Durch diese Datenbasis lassen sich viele Dinge sehr einfach und elegant formulieren. Allerdings stellt sie auch globalen Zustand dar – mit den üblichen Nachteilen globaler Variablen. So muss man z.B. bei Tests selbst dafür sorgen, dass die Datenbasis aufgeräumt wird. Positiv an der Datenbasis ist allerdings, dass nach Programmausführung bzw. Test die Daten für Post-Mortem-Debugging verfügbar sind.

Hat jemand andere oder ergänzende Erfahrungen? Dann immer her damit!

Beispiel für Closure

Gerade habe ich einen Anwendungsfall für Closures. Ich habe sequenziellen Code a la:

tueA();
tueB();
tueC();

Ich möchte gerne mitloggen, wie lange die einzelnen Schritte dauern (um das Beispiel zu vereinfachen, logge ich durch Ausgabe auf die Konsole):

long startTime = System.currentTimeMillis();
tueA();
System.out.println("duration in sec: " +  ((System.currentTimeMillis() - startTime) / 1000));

startTime = System.currentTimeMillis();
tueB();
System.out.println("duration in sec: " +  ((System.currentTimeMillis() - startTime) / 1000));

startTime = System.currentTimeMillis();
tueC();
System.out.println("duration in sec: " +  ((System.currentTimeMillis() - startTime) / 1000));

Das funktioniert, nur den eigentlichen Code kann man kaum wiederfinden. Wenn ich Closures (wie z.B. in Groovy) zur Verfügung habe, ist es ganz einfach:

def timeLogged (Closure c) {
    long start = - System.currentTimeMillis()
    c.call()
    println "duration in sec: " + (System.currentTimeMillis() + start) / 1000;
}

timeLogged { tueA() }
timeLogged { tueB() }
timeLogged { tueC() }

Das ist kürzer, redundanzfreier und meiner Meinung nach deutlich besser lesbar.

P.S.: Redundanzfrei kann es ohne Closures in Java natürlich auch hinkriegen, mit anonymen Inner Classes. Dummerweise wird der Code dann noch länger und noch schwerer lesbar.

BDD mit Test-Technologien

Vor kurzem habe ich einen kurzen Vergleich zwischen TDD und BDD gewagt und BDD präferiert. Bernd Schiffer meinte, die TDD-Tests seien suboptimal und wenn man sie verbessert, sei TDD genauso gut wie BDD. Das sehe ich ähnlich. Man bekommt das vor allem dadurch hin, dass man BDD-Ansätze übernimmt. Und wie das geht, will ich in diesem Beitrag zeigen.

Tests je Fixture und nicht je Klasse
Der erste Punkt ist, dass man Tests um Fixtures (getestetes Objekt bzw. Objektgeflecht) gruppiert und nicht um Klassen. Diese Forderung gibt es auch schon lange in TDD, wird aber meist ignoriert. Die meisten Unittests werden um Klassen gruppiert. BDD erzwingt die Gruppierung um die Fixture durch sein Benennungsschema (a la it „should create new meetings“). Und so ein Benennungsschema kann man sich auch in Unitttests geben. Wir haben sehr gute Erfahrungen damit gemacht, die Testklasse wie die Fixture mit angehängtem „Test“ zu benennen und die Testmethoden so, dass sie zusammen mit dem Fixturenamen einen Satz ergeben. Das sieht dann in Java z.B. so aus:

public class TerminkalenderTest {
    @Test
    public void erzeugtNeueTermine() {...}
}

Diese Art der Methodenbenennung ist zumindest in Java unüblich, aber der Nutzen überwiegt hier meiner Meinung nach deutlich. Mit TestDox gibt es übrigens ein Tool, dass aus solchen Tests Anforderungsspezifikationen in Prosa generiert in der Art:
Terminkalender

  • erzeugt neue termine

Nun ist „Terminkalender“ natürlich erstmal eine ziemlich langweilige Fixture. So beginnt es häufig. Mit der Zeit wird im Test aber sehr deutlich, dass eine neue Fixture raus will. Das kann man leicht daran erkennen, dass in den Testmethoden zuerst die Fixture verbogen oder nur ein Teil der Fixture im Test benutzt wird.

Assertions
Der zweite Punkt betrifft die Überprüfungen im Test. xUnit bietet die klassischen assertXXX-Methoden an. In den neuen Versionen von JUnit findet sich mit Hamcrest eine Framework, um die Prüfungen in BDD-Stil beschreiben zu können. Mit RSpec heißt es z.B. termin.start_zeit.should == @jetzt. Mit Hamcrest für Java heißt es assertThat(termin.getStartZeit(), equalTo(jetzt));. Da hat man jetzt jede Menge nervige Klammern, aber das liegt an Java und nicht an TDD.

Fazit
Wenn man aus welchen Gründen auch immer Unittest-Frameworks einsetzen will oder muss, kann man viel von BDD auch für TDD adaptieren. Das geht immer und lohnt sich auf jeden Fall.