Sonntag, 30. September 2007

Dieser Blog ist tot. Ich blogge weiter auf dem «Agile Trail».

Berlin und Biel ist viel zu viel

Mein 100er von Biel ist jetzt 14 Wochen her - nicht lange genug. Mehr aus Trotz wollte ich heute die Drei-Stunden-Marke beim Berlin-Marathon angehen, aber leider hat nur Haile eine neue Bestzeit geschafft.

Man soll sein Glück ja auch nicht herausfordern. Drei Wochen nach Biel habe ich das Training für Berlin aufgenommen. Zuerst hat das ja auch gut geklappt: Am Ende der zweiten Trainingswoche bin ich eine neue Bestzeit über 10 km gelaufen (38'09). Allerdings hatte ich da auch schon diejenigen Beschwerden, die mich heute haben scheitern lassen, nämlich eine Sehnenreizung im linken Bein.

Die Schmerzen wurden dann irgendwann so stark während des weiteren Trainings, dass ich zum Orthopäden ging und schließlich beim Physiotherapeuten (Frank Schmelcher aus Karlsruhe, sehr zu empfehlen!) landete. Das war noch Glück im Unglück, denn ich habe dort viele gute Tipps fürs Training bekommen.

Dummerweise habe ich nach der ersten Behandlung in der Physiotherapie entweder eine Gürtelrose bekommen oder aber eine immens starke Haarwurzelentzündung: Die Diagnose hing vom jeweiligen Arzt ab :-/ Wie auch immer: kompletter Trainingsausfall von anderthalb Wochen und danach enorm eingeschränktes Training.

Und die Arbeit: Seit vier Wochen läuft meine Batterie auf Reserve, und in diesen Wochen habe ich zwei Schulungen gegeben, was nicht sehr entspannt. Urlaub kommt zwar, aber leider passte der privat und beruflich nicht mehr vor Berlin. Es gibt nichts besseres für einen Wettkampf, wenn man sich vorher gut erholen kann. Das ist weitaus wichtiger, als hinterher die Beine hochzulegen. Das alles fällt einem besonders dann auf, wenn man bewusst Stress erfährt, während einem die Zuschauermassen in Berlin zujubeln und man denkt 'Boah, wann hört denn dieser Krach auf - das neeeervt!!'

Na, wenn ich mir das jetzt so anschaue, dann hätte ich viel Geld und Engagement sparen können, wenn ich Berlin ausgelassen hätte. Hätte, könnte, sollte, jetzt egal.

Gestern, Samstag, bin ich von Karlsruhe nach Berlin via ICE gefahren und noch auf die Berlin Vital zum Startunterlagen abholen gegangen. Abends habe ich mich dann vorm Fernseher ausgeruht und die Sachen für den nächsten Tag vorbereitet. Zum Glück hat unsere Firma in Berlin Wohnungen, die nur unter der Woche genutzt werden - oder wenn ein Kollege eben Marathon läuft :-) Danke von dieser Stelle an Henning für die tolle Schlüsselübergabe!

Als ich heute morgen aufgestanden bin, war ich hundemüde, obwohl ich acht Stunden Schlaf hinter mir hatte. Kein gutes Omen. Nach dem Frühstück bin ich gleich los, von der Wohnung in der Nähe der Messe (ICC) zum Marathon-Start-Ziel-Bereich zwischen Hauptbahnhof, Reichstagsgebäude und Brandenburger Tor. Boah, was für Menschenmassen. Wieder über 40000 Meldungen hat's dieses Jahr gegeben, und der Laufpöbel will versorgt werden: Umziehplätze, Kleiderbeutelverwahrstellen, Toiletten, ... - ja, diese blöden Toiletten sind nie in ausreichender Anzahl vorhanden, gleich welche Anzahl da rumsteht. Es war knapp, aber trotzdem war ich pünktlich am Start in Block C. Es gibt acht Blöcke, A bis H, wobei C derjenige ist mit den Läufern im Bereich 2:50 h bis 3:00 h. Gestartet wurde um Punkt 9:00 h, und zwar die Blöcke A bis C. Der Rest rückt dann auf und macht einen zweiten Start mit, zwei Minuten später als der erste. Und es gibt noch einen dritten Start, noch mal zwei Minuten nach dem zweiten Start. Alles, damit sich die Leute nicht tottrampeln.

Das lief von Anfang an nicht rund bei mir. Normalerweise ergibt sich nach ein paar Kilometern so ein Flow-Gefühl das signalisiert, dass ich gut drauf bin. Pustekuchen, nix da mit Flow! Ich hätte genau so gut die ganze Zeit auf Kopfsteinpflaster laufen können, dass wäre ähnlich flüssig vom Bewegungsablauf gewesen. Naja, immerhin waren die Zeiten gut: die ersten fünf km lag ich bei 20'57, dann 21'17, 21'06 und 21'30. Die erste Hälfte bin ich in 1:29'40 gelaufen - zu langsam, denn normalerweise brauche ich ein 49:51-Verhältnis, weil ich hintenraus langsamer werde. Das wären dann 1:28'12 auf den Halben gewesen und 20'54 pro 5er-Abschnitt. 'Egal', denke ich mir, 'dann machst Du von jetzt bis Kilometer 30 ein wenig Druck', und vielleicht würde ich einen Puffer aufbauen können.

Von Kilometer 21 bis 22 bin ich exakt 4'15 gelaufen (das ist in etwa der Kilometerschnitt den man für < 3:00 h anpeilen muss. Von Kilometer 22 bis 23 wollte ich, wie beschrieben, Druck machen - und bin eine 4'16 gelaufen! Grrr, das muss doch gehen, also noch mal mehr Druck von Kilometer 23 bis 24: 4'17. Da war's dann vorbei mit der Psyche.

Ab Kilometer 10 hatte ich Schmerzen im linken Fuß, was nervig, aber nicht schlimm war. Ab Kilometer 12 gesellte sich zum Fuß die Sehne hinzu, die mir das Training versaut hat. Und ab Kilometer 15 oder so machte der Magen-Darm-Trakt bei der Party mit. Bei Kilometer 24 kam dann alles zusammen: die Körperteileparty und mein Unvermögen, trotz vollem Aufgebot aller Energiereserven die Kilometerzeitvorgabe zu halten.

In Berlin gibt es viele U-Bahn-Stationen. Von denen lungern zig an der Marathonstrecke rum. Welch eine Versuchung für mich ab Kilometer 24! Aber ich war zu neugierig auf die Strecke, und überhaupt: Ich habe schon einmal einen Marathon zu viel abgebrochen und das macht man einfach nicht. Ehre und so.

Der Plan A war ursprünglich, dass ich nach Berlin fahre und mal die 3 h zu knacken versuche. Plan B sah vor, dass, sollte ich tatsächlich nicht Plan A umsetzen können, ich locker auslaufe ins Ziel hinterm Brandenburger Tor. Auslaufen? Hallo? Womit ich gar nicht gerechnet habe: Ich musste richtig kämpfen, um dabei zu bleiben! In Freiburg bin ich dieses Jahr schon tüchtig daneben gelegen, was meine Zielzeit anging (unter 3 h angepeilt, 3:16'30 ist's geworden), aber da bin ich trotzdem eine 3:15 gelaufen.

In Berlin nun habe ich mich dann ziemlich ins Ziel gequält, insgesamt fast zwei Stunden auf der zweiten Marathonhälfte. Sehnenschmerzen, Gehpausen (!), Schleichabschnitte, Unter-5'00-Abschnitte, und wieder von vorne. Nee, das machte echt keinen Spaß, tolle Strecke hin oder her. Nach einer gefühlten Ewigkeit bin ich dann mit einer Nettozeit von 3:26'46 ins Ziel gekrochen. 'Immerhin überhaupt angekommen', denke ich mir jetzt so im Nachhinein. Der Marathon in Berlin ist sehr schön: gute Organisation, viele Menschen an der Strecke (wenn man sie aushalten kann ;-) ), tolle Streckenführung. Hinzu kam dieses Jahr ein klasse Wetter (12°; bedeckt, aber kein Regen). Hätte gerne selbst erfahren, wie schnell die Strecke ist, aber dieses Gefühl wollte sich irgendwie nicht einstellen... Leider habe ich kein Finisher-Shirt mehr bekommen. Man hätte vorbestellen sollen, habe ich vom Infostand erfahren. Es weiss nicht zufällig jemand, wie ich noch an eines herankommen könnte?

So, jetzt mache ich erstmal Urlaub, zwei Wochen ausspannen (ja, ich weiß, hätte ich vor Berlin machen sollen), und dann werden die Pläne für die nächsten 12 Monate geschmiedet. Für dieses Jahr soll's das auch erstmal gewesen sein (naja, vielleicht noch einen kleinen 10er oder so). (Vorläufige) Bilanz für 2007:

  • Fünf Marathons oder drüber: Freiburg-Marathon, Rhein-Ruhr-Marathon (Trainingslauf), 50-km-Westerwaldlauf (Trainingslauf), 100 km von Biel, Berlin-Marathon
  • Neue Bestzeit über 10 km (38'09 im Schwarzwälder Bühlertal; hügelig: da stecken noch bessere Zeiten drin auf ebener Strecke!)
  • Neue Strecken gelaufen: 50 km (Westerwaldlauf in 4:43) und 100 km (Biel in 10:34'55) und damit meine ersten Ultras überhaupt
Was lerne ich vom Berlin-Marathon und der Vorbereitung darauf:
  • Unterschätze nicht, wie Dich ein 100er auslaugt! Ich war nicht bereit für das Training auf einen Marathon unter drei Stunden drei Wochen nach Biel. Wenn ich nächstes Jahr Biel laufe, werde ich danach keine Bestzeiten jagen.
  • Das Training von Ultra und Marathon unterscheiden sich sehr: Ist das Ultratraining von Quantität geprägt (sehr viele Kilometer, sehr lange Läufe), so ist das Marathontraining von viel mehr Tempoeinheiten durchsetzt (Tempoläufe, Intervallläufe, Fahrtspiele, Bahntraining, Steigerungen, Intensive Marathonkontrollläufe). Gerade diese Tempoeinheiten haben mir, respektive meiner Sehne, sehr zu schaffen gemacht. Ich darf beim nächsten Mal nicht mit so viel Tempoeinheiten vom Ultratraining zum Marathontraining wechseln.
  • Beim Wechsel von Ultra- auf Marathontraining konzentriere Dich stärker auf den Aufbau der Muskulatur durch entsprechende Übungen: Gymnastik, Wackelbrett, Lauf-ABC. Sonst haut's Dich bei den Tempoeinheiten aus der (Trainings-)Kurve.
  • Urlaub sollte vor einem Wettkampf liegen, mindestens drumherum, auf keinen Fall danach. Kann ich meinen Urlaub nicht verlegen, verlege ich mich selbst auf einen anderen Wettkampf. Berlin wäre auch nächstes Jahr noch attraktiv gewesen.
  • Enthaarte Beine verhindern Haarwurzelentzündungen :-)

Samstag, 29. September 2007

Dieser Blog ist tot. Ich blogge weiter auf dem «Agile Trail».

Semi-Automatische Folien mit Groovy

Letzte Woche habe ich eine dreitägige Grails-Schulung gegeben. Ziel dieser Schulung war, dass die Teilnehmer einfache Webanwendungen nach der Schulung selbst schreiben können. Es ist wichtig, dass die Teilnehmer zum einen die Grundlagen von Grails verstehen und zum anderen wissen, wo und wie sie den Rest selbst herausfinden können. Zu den Grundlagen zählt z.B. das Wissen um das Model-View-Controller-Pattern (MVC) und wie man es in Grails benutzt, aber auch natürlich Groovy, denn in Grails macht man in der Regel häufig Gebrauch davon. Spätestens, wenn man mehr Informationen über Grails haben möchte, hilft die Sourcecodeexegese, wobei man hier an Groovy nicht mehr vorbeikommt. Daher gliedert sich die Schulung auch in einen Teil Groovy (der erste Tag) und einen Teil Grails (die verbleibenden zwei Tage).

Natürlich kann man an einem Tag nur einen kleinen Teil von Groovy vermitteln, etwa die häufigsten Sprachfeatures wie Closures und Operator-Overloading sowie Teile des GDKs, also der Groovy-API. In der Vorbereitung dieser Schulung habe ich also Grails und Grailsanwendungen durchforstet nach Groovy-Eigenschaften, die der gemeine Grailsentwickler kennen sollte. Und das ist eine ganze Menge - zumindest, wenn man mehr machen möchte als Scaffolding ;-)

Wie vermittelt man das notwendige Wissen um Groovy, wenn man als Teilnehmer Tag 2 und 3 der Schulung überstehen möchte? Ein Weg wäre, einen reinen Theorievortrag zu halten und die Groovy-Spec quasi in Folien zu gießen - aber das wäre langweilig, für mich als Trainer, beim Vorbereiten der Schulung und dem Vortragen der Folien, und für die Teilnehmer sowieso. Außerdem hat das wenig Praxisbezug, was die Transferleistung erhöht, das Gelernte in Grails umzusetzen.

Dierk König und Co-Autoren machen gute Wissensvermittlung in GinA vor: Beispiele noch und nöcher! Daran habe ich mir ein Beispiel genommen und die Präsentation für Tag 1 fast ausschließlich über kleine und überschaubare Codeschnipsel gestaltet. Dabei steht auf den Folien außer der Folie kein weiterer Text, nur Code. Was es zu dem jeweiligen Beispiel zu sagen gab, habe ich gesagt. So sind etwa 90 Folien mit Code zustandegekommen, die zusammen mit Übungen den ersten Tag der Schulung in einen Workshop verwandelten. So sieht eine Beispielfolie aus:
Wer hat schon Lust, 90 mal Code auf Folien zu schreiben? Typischerweise schreibt man diese Codeschnipsel in einem Editor, wo man ihn auf Funktion prüfen kann, und copy-paste-d ihn dann nach Kraftpunkt, einschließlich Syntaxhighlighting. Dieses Verfahren impliziert, dass ich pro Folie eine Datei mit dem entsprechenden Code anlege. So weit, so langweilig, so naja.

Der Haken ist der, dass der Code sich ändern kann! So aus dem Bauch heraus würde ich schätzen, dass ich Foliencode pro Folie etwa drei Mal ändere im Laufe der Vorbereitung einer Präsentation. Bald kommt Groovy 1.1 raus, dann gibt's auch wieder haufenweise Verbesserungen, die ich in den Folien ändern muss. Und schließlich mache ich Fehler, und ein Reviewer (oder aufmerksamer Schulungs-Teilnehmer) hat immer Korrektur- und Verbesserungsvorschläge.

Änderungen ziehen mit jenem manuellen Verfahren Probleme nach sich: Wie halte ich die Folien synchron zu den Dateien mit den Codeschnipseln? Nur manuell, und damit wird's stupide und fehleranfällig: Ich möchte an einer Folie etwas im Code ändern, also suche ich mir die entsprechende Datei raus, ändere und überprüfe den Code in der Datei und dann copy-paste ich den Code wieder in die Folie.

Nee, das macht keinen Spaß, ist unökonomisch (die vielen manuellen Schritte, immer und immer wieder), fehleranfällig (was dabei alles schief gehen kann) und überhaupt total ungroovy. Ich möchte die Synchronisation automatisieren. Außerdem möchte ich die Schnipsel automatisiert testen können. Und schließlich möchte ich die Schnipsel klein halten: Als brauchbar haben sich maximal 55 Zeichen pro Zeile und maximal 15 Zeilen pro Folie erwiesen. Da läuft man ohne Hilfe schnell aus dem Folienrahmen, aber merkt das erst nach dem Copy-Paste-Vorgang, also zu spät. Das ist vielleicht ein Gefummel, kann ich Euch sagen!

Wie automatisiere ich das nun? Naiver erster Versuch: Textdatei mit Groovy-Skript in Kraftpunkt als Verknüpfung referenzieren. Geht aber nicht, denn Kraftpunkt kann den Text nur importieren, was den Bezug zur Datei verloren gehen läßt. OpenOffice ist da keinen Deut besser. Wenn's doch gehen sollte, hab ich's nicht hinbekommen. Und außerdem gibt's mit diesem Verfahren kein Syntaxhighlighting, und ich will niemandem 90 Folien voll Code ohne Farben zumuten.

Ich hab's dann doch geschafft, und so sieht der Weg aus:


  1. Ich erstelle ein normales Groovyscript mit einem Codeschnipsel drin, z.B. für die Fälle bei der Groovy-Truth.

  2. Dann lasse ich ein Magic-Build-Groovy-Skript laufen und erhalte eine Grafik im PNG-Format, die den gleichen Namen hat wie das entsprechende Skript.

  3. Diese Grafik verknüpfe ich in Kraftpunkt. Fertig.


Wenn ich jetzt den Codeschnipsel ändern möchte, dann ändere ich das entsprechende Groovy-Skript, werfe den Magic-Build-Vorgang an und mehr nicht: Kraftpunkt merkt die Veränderung und aktualisiert die Folien entsprechend.

Aber so richtig interessant wird's, wenn man bedenkt, was da eigentlich passiert: Das Magic-Build-Groovy-Skript durchforstet ein Verzeichnis nach Groovy-Dateien, den Codeschnipseln aus der Folie. Diese werden gelesen, volumengeprüft, übersetzt und evaluiert, also intern übersetzt. Hätten meine Schnipsel zu lange Zeilen oder zuviele davon, dann würde ich das bei der Methodenprüfung merken. Gäbe es Compile-Fehler, dann würde ich das beim Übersetzen merken. Wären meine Inlinetests (die vielen asserts) kaputt, dann würde ich das beim Evaluieren merken. Rundumsorglos abgesichert :-) Schließlich werden aus den Textdateien png-Grafiken generiert und diese in ein anderes Verzeichnis geschrieben.

Wie's funktioniert zeigt die Power von Groovy: das Lesen der Dateien ist ein simples new File('pfad/zum/skript.groovy').text. Die Volumenprüfung erfolgt ebenfalls im Skript mit Groovy (ein paar mehr Codezeilen, aber sehr überschaubar). Das übersetzen und evaluieren schafft man wieder mit einem Einzeiler:
evaluate(skriptAlsString)

Den Teil mit dem Bildgenerieren habe ich dann wieder in Java geschrieben und vom Magic-Build-Groovy-Skript aus aufgerufen. Beim Bildermalen gibt's fast keine Logik, sondern nur viele API-Calls gegen Javas AWT, wobei Codecompletion doch recht praktisch ist, was in Java eben noch besser geht als in Groovy. Toll ist, dass ich einfach wechseln kann, wenn ich das möchte, dank Groovy und dessen grandioser Interaktion mit Java.

Das Syntaxhighlighting erfolgt auch noch in Java (selbstgeschrieben), aber das möchte ich nach Groovy portieren vor der nächsten Schulung - es wäre dort einfacher zu schreiben und weiterzuentwickeln. Da ich von Java aus auch Groovy aufrufen kann, mache ich mir auch keine Sorgen, dass ich damit Probleme bekommen könnte :-)

Der aufmerksame Leser wird auf der Folie weiter oben diese Zeile bemerkt haben:
assert new Bar() && !null

Wo kommt denn da das Bar her? Wie kann denn das übersetzt werden? Das passiert mit einem kleinen Trick. Das Skript hierzu sieht so aus:
assert new Bar() && !null
/* ### */
class Bar {}

Bei der Evaluierung wird das komplette Skript betrachtet. Daher gibt's keine Fehler aufgrund desssen, dass die Klasse Bar nicht hätte gefunden werden können. Die drei Rauten (###) geben dem Skript allerdings an, diesen Kommentar und alles Nachfolgende nicht in das Bild zu schreiben. Ähnliches gibt's auch mit drei Pfeilen nach oben (^^^), was mir Code vor dem Skript aus Bildern ausblendet. So einfach ist ein SetUp und TearDown selbstgeschrieben.

Der Nachteil bei diesem Verfahren ist, dass die Teilnehmer keinen Code aus den Folien kopieren können, um ihn mal eben in der GroovyConsole auszuprobieren. Da die Codeschnipsel aber auch nicht groß sind, ist das nicht so schlimm. Schlimmer finde ich, dass die Teilnehmer nicht digital suchen können in den Folien: es sind eben Bilder, kein Text.

Trotzdem, diese Nachteile überwiegen bei Weitem nicht die Vorteile, die sich mit diesem Verfahren ergeben: automatische Synchronisation, automatischer Test (Übersetzen und Inline-Tests), Syntaxhighlighting und Volumenprüfung.

Ausbaufähig wäre das ganze allerdings noch: Ich muss immer noch nach dem entsprechenden Groovy-Skript für eine Folie suchen. Dabei behelfe ich mir zwar momentan mit einer Namenskonvention, aber das Wahre ist das auch noch nicht (es sei denn, man hätte ein automatisches Rename-Refactoring für Skripte und Folien - ich bin aber auch verwöhnt... ;-) ) Und eine neue Folie muss ich immer noch selbst anlegen mit einem Bild und einer Überschrift.

Ich hab mich in Groovys Scriptom immer noch nicht eingearbeitet (siehe auch GinA S. 546ff.), aber ich hoffe, das Folgende damit tun zu können:

  • Zu einem Verzeichnis mit png-Bildern wird pro Bild eine Folie mit einem bestimmten Layout angelegt. In diese Folie wird das Bild eingetragen als Verknüpfung und der Name des Bildes als Überschrift (CamelCase.png wird dann zur Überschrift CAMEL CASE).

  • Gibt es eine Überschrift, die nicht zu einer Bilddatei passt, dann wird die Folie gelöscht.

  • Gibt es eine Bilddatei, die nicht zu einer Überschrift gehört, dann wird eine neue Folie angelegt.

  • Gibt es genau eine Folie, zu der es keine Bilddatei gibt, und gibt es genau eine Bilddatei, zu der es keine Folie gibt, dann wird an der Stelle der bilddateilosen Folie eine neue Folie mit dem folienlosen Bild erzeugt. (Rename-Refactoring!!).



Bin mal gespannt, ob das mit Scriptom zu schaffen ist. Bin dankbar für jeden hilfreichen Hinweis, wie man mit Scriptom Kraftpunkt-Präsentationen manipuliert.

Schamlose Werbung: Die nächsten Schulungen mit Stefan und/oder mir in Hamburg für Groovy und für Grails stehen bereit. Termine und Inhalte gibt's hier für Groovy und hier für Grails.

Dienstag, 25. September 2007

Dieser Blog ist tot. Ich blogge weiter auf dem «Agile Trail».

Schnapsidee: xUnit.net ohne SetUp und TearDown

James Newkirk schreibt in seinem Blog, warum man nicht SetUp und TearDown in Tests nutzen sollte, Brad Wilson denkt ebenfalls darüber nach - und mich haut's aus TDD-Sicht lang hin bei der Argumenation!

Wir sind in der .NET-Welt. SetUp und TearDown sind zwei Methoden einer Testklasse des xUnit-Frameworks NUnit, die vor bzw. nach jeder Testmethode ausgeführt werden. SetUp und TearDown haben bei NUnit noch einen anderen Stellenwert als bei JUnit. Das kommt daher, dass bei NUnit nicht für jede Testmethode eine eigene Instanz der Testklasse erzeugt wird, wie dies bei JUnit der Fall wäre. Daher können keine Instanzvariablen bereits bei der Deklaration initialisiert werden, sondern man ist gezwungen, sie im SetUp zu initialisieren. Dadurch wird der Code aufgebläht. Wenn ich solche JUnit-Testklassen sehe...

public class FooTest {
private Foo foo;
protected void setUp() {
foo = new Foo();
}
// Testmethoden
}
... dann wird da ganz schnell soetwas draus:
public class FooTest {
private Foo foo = new Foo();
// Testmethoden
}
In NUnit wäre das so nicht möglich. Was wollte man nun ändern, würde man ein neues Testframework für die .NET-Welt schaffen? Richtig, die Instanziierung von Testklassen pro Testmethode. Newkirk und Wilson wissen noch eine weitere Innovation: Sie entwickeln ein neues Testframework namens xUnit.net ohne (!) SetUp und TearDown, wie InfoQ zu berichten weiß.

In ihren Blogeinträgen argumentieren Newkirk und Wilson unter anderem, dass man bei gegebenen SetUp- und TearDown-Methoden pro Test in drei Methoden reinschauen müsse und dass man dazu tendieren würde, auch nicht gemeinsam genutzte Attribute der Testmethoden ins SetUp bzw. TearDown zu packen und damit gegen das Single Responsibility Pattern verstoßen würde. Dabei zeigt Newkirk eine Testklasse, an der er das Problem verdeutlichen würde.

[TestFixture]

public class MoneyTest
{
private Money f12CHF;
private Money f14CHF;
private Money f7USD;
private MoneyBag moneyBag;

[SetUp]
public void BeforeTest()
{
f12CHF = new Money(12, "CHF");
f14CHF = new Money(14, "CHF");
f7USD = new Money(7, "USD");
moneyBag = new MoneyBag(f12CHF, f7USD);
}

[Test]
public void IsZero()
{
Money[] bag = { new Money(0, "CHF"), new Money(0, "USD") };
Assert.IsTrue(new MoneyBag(bag).IsZero);
}

[Test]
public void BagAdd()
{
Money[] bag = { new Money(26, "CHF"), new Money(7, "USD") };
MoneyBag expected = new MoneyBag(bag);
Assert.AreEqual(expected, f14CHF.Add(moneyBag));
}

[Test]
public void BagSubtractIsZero()
{
Assert.IsTrue(moneyBag.Subtract(moneyBag).IsZero);
}
}

Diese Klasse hat ganz klar einen Fehler, aber es ist nicht der, dass sie SetUp- und TearDown-Methoden besitzt, sondern dass sie zuviele unterschiedliche Dinge testet in einer einzigen Testklasse. Oder anders ausgedrückt: da sind mehrere Testfixtures vermischt.

Frank Westphal definiert Testfixtures so:
Ein Testfall sieht in der Regel so aus, daß eine bestimmte Konfiguration von Objekten aufgebaut wird, gegen die der Test läuft. Diese Menge von Testobjekten wird auch als Test-Fixture bezeichnet. Pro Testfallmethode wird meist nur eine bestimmte Operation und oft sogar nur eine bestimmte Situation im Verhalten der Fixture getestet.
Und genau diese Testobjekte werden in dem von Newkirk gezeigten Code alle zusammen in die SetUp-Methode gepackt. Tatsächlich ist hier die SetUp-Methode überflüssig, weil die Fixtures keine Gemeinsamkeiten aufweisen. Aber dieser eine Fall zeigt mitnichten, dass die SetUp-Methode selbst überflüssig wäre. Wie sieht's denn mit gemeinsamen Fixtures aus? Sollte dann wirklich vor jeder Methode die gleichen Testobjekte erzeugt werden? DRY würde sich wundern: Wenn es beklagenswert wäre, drei Methoden im Blick zu haben, wie beklagenswert wäre es denn, jedesmal das gleiche im ersten Teil einer jeden Testmethode zu lesen? Wie nervig wäre es, wenn die Fixture angepasst werden müsste, und dies nicht an einer, sondern an zig Stellen passieren müßte? Und wie wollte man überhaupt die Fixtures erkennen und als solche identifizieren, wenn diese implizit in den Testmethoden versteckt sind?

Die richtige Antwort auf das Problem einer verkorksten Fixture wäre, mehrere Testklassen aus dieser einen hier zu extrahieren, sofern sich mehrere Fixtures herausbilden. Typischerweise orientieren sich die Testklassen eben genau an den Fixtures. Wer hier Testklassen zu getesteten Klassen im Verhältnis 1:1 schreibt, bringt sich um diese wichtige Erkenntnis der Fixtures und verletzt dann tatsächlich das Single Responsibility Prinzip. Ergo: Höre auf die Fixtures. Sie erzählen Dir, welche Testklassen Du brauchst.

Newkirk und Wilson argumentieren aber nicht nur über die Lesbarkeit oder den Mißbrauch von SetUp- und TearDown, sondern auch darüber, dass ohne SetUp/TearDown die Isolation der Tests nicht mehr gefährdet sei. Tatsächlich ist das ein Problem in der NUnit-Welt aufgrund oben genannter Nicht-Instanziierung der Testklassen pro Testmethode: Eine Instanzvariable lebt über alle Testmethodenausführungen einer Testklasse hinweg. Ohne SetUp und TearDown kann man keine Instanzvariablen mehr erzeugen, ohne die Tests voneinander abhängig zu machen, ergo ist das Abschaffen dieser Methoden eine gute Sache. Einspruch: Das ist keine gute Sache! Warum nicht einfach Instanzen pro Testmethode erzeugen, wie es viele andere xUnit-Frameworks auch tun? Tatsächlich ist dies auch ein Feature von xUnit.net, und vor diesem Hintergrund ist es mir unbegreiflich, warum SetUp und TearDown abgeschafft werden sollen.

Eine ganz wichtige Sache geht bei diesem Unfug auch noch über Bord: das Aufräumen nach dem Fehlschlagen von Tests. Wenn eine Zusicherung in einem Test fehlschlägt, dann wird garantiert, dass die TearDown-Methode ausgeführt wird. Wenn ich aber keine TearDown-Methode mehr habe, wie soll ich dann aufräumen, was noch offen rumliegt? Auf diese Art schaffe ich unangenehme Seiteneffekte: Test A öffnet eine Datei, sichert Dinge zu und schließt die Datei wieder. Ähnlich Test B: Dieser Test öffnet dieselbe Datei, sichert andere Dinge zu und schließt diese Datei ebenfalls wieder. In diesem Szenario wäre das Öffnen der Datei im SetUp und das Schließen im TearDown zu finden - wenn man denn diese Methoden hätte. Hat man sie nicht, dann steht entsprechender Code am Anfang und Ende von Test A und B. Angenommen, eine Zusicherung knallt, dann wird nachfolgender Code nicht mehr ausgeführt, die Datei also nicht mehr geschlossen für den Test, der danach kommt und der dann ebenfalls knallt. Super, wir bauen uns eine Abhängigkeit zwischen den Tests und hin ist sie, unsere Testisolation.

Merke: SetUp und TearDown sind gut. Es macht xUnit.net nicht zu einem besseren Testframework, Fixtures nicht mehr zuzulassen.

Und irgendwie scheinen Newkirk und Wilson das auch zu wissen, so ganz tief im Innern:
We believe that use of [SetUp] is generally bad. However, you can implement a parameterless constructor as a direct replacement. [...] We believe that use of [TearDown] is generally bad. However, you can implement IDisposable.Dispose as a direct replacement.
Was bedeutet das jetzt? SetUp ist schlecht, aber wenn man es doch braucht, dann kann man das gewünschte Verhalten implizit über den Konstruktor erreichen? TearDown ist schlecht, aber wenn man es doch braucht, kann man es explizit über ein komplizierteres Konstrukt erreichen?

InfoQ fragte: "xUnit.net - Next Generation of Unit Testing Frameworks?" Ich meine: Nein, kein Fortschritt, sondern ein Rückschritt!

Freitag, 21. September 2007

Dieser Blog ist tot. Ich blogge weiter auf dem «Agile Trail».

Groovy 1.1 beta 3 und MOP

Groovy 1.1 beta 3 ist seit heute draussen. Da gibt es so kleine Schmankerl wie die Aufbohrung des ternären Operators: Schrieb man bisher

a != null ? a : "default value"
, so kann man sich Redundanz und Tipparbeit sparen durch die Notation
a ?: "default value"

Und es wird immer interessanter, mit dem Meta-Object Protocol (MOP) zu arbeiten. Zwischen dem Umgang mit MOP in Groovy 1.0 und 1.1 beta 3 liegen mitlerweile Welten: wer mag noch eine Category schreiben, wenn er ExpandoMetaClasses zur verfügung hat?

Zu verdanken ist dies hauptsächlich dem Grails-Projekt und dessen Project Lead Graeme Rocher. In seinem aktuellen Blogeintrag beschreibt er prägnant und eindrucksvoll einen Teil dessen, zu was das neue MOP in der Lage ist.