Sonntag, 8. März 2009

Behaviour-Driven Development (BDD) and Syntactic Shugar

Da ich gerade mein Abalone Projekt neu aufsetze, will ich das Ganze dieses mal test-driven entwickeln. Dabei will ich, wenn es um das Verhalten von Controlern und diverser Objekte geht auf den BDD-Style setzen. Bei diesem Style geht es darum, dass Tests weniger wie Tests aussehen, sondern eher wie die Spezifikation des Systems. So beschreibt ein BDD-Test eine Aktion und was als Ergebnis erwartet wird.

Man hätte dann etwas in folgender Form:

When the application was told to register a new user
-a new user will be created
-a register email will be send
-a success message will be shown

Dahinter würden sich nun entsprechende Methodenaufrufe verbergen die verifizieren ob die Anforderungen eingehalten werden. Das hat den Vorteil, dass man noch nach einer langen Zeit genau weiss, wie sich das Objekt verhalten sollte. Der andere Vorteil ist, dass man dem der die Anforderungen stellt (z. B. der Kunde oder Chef), die Tests als Protokoll zeigen kann.

Ich komme jetzt gerade auf das Thema, weil ich dabei bin Anforderungen für mein Eingangsportal zu sammeln. Jetzt wollte ich anfangen den ersten BDD-Test für mein Projekt zu schreiben. Da ist mir aufgefallen, dass ich noch keine BDD-Lib habe um eine schöne Lesbare Syntax hin zu bekommen. Vor allem für das Setup und Asserts von gemockten Objekten habe ich mir überlegt wie kann man eine schöne lesbare Syntax hinbekommen. In Anlehnung an schon bekannte Frameworks habe ich mir für das Setup Folgende Syntax überlegt und in einem standard Unit-Test spezifiziert:

[Test]
public void when_a_mockobject_was_told_to_return_a_value_it_will_return_it() {
var mockRepository = new MockRepository();
var testobject = mockRepository.Stub<ITestDependency>();

When.The(testobject).was_told_to(x => x.DoSomething(4)).Then.Return("success");

mockRepository.ReplayAll();
string result = testobject.DoSomething(4);
result.will_be_equal_to("success");
mockRepository.VerifyAll();
}

public interface ITestDependency
{
string DoSomething(int value);
}

Also ich finde, dass die Zeile
'When.The(testobject).was_told_to(x => x.DoSomething(4)).Then.Return("success");'
schon sehr fluffig aussieht. Was man nicht alles für Spielereien machen kann :-)

Sonntag, 1. März 2009

Refactoring die Zweite

Durch meinen guten Kollegen Stefan Lieser bin ich auf diese Seite gestoßen. Dort geht es darum, eine Enumeration nicht als enum zu verwenden, sondern eine Klasse zu bauen mit statischen Enumerations-Objekten. Die Klasse lässt sich verwenden wie eine normale Enumeration hat aber den Vorteil, dass man noch zusätzliche Attribute hinterlegen kann. Ich fand die Idee sehr interessant und habe mir überlegt, ob ich das für mich irgend wie einsetzen kann. Ursprünglich hatte ich für die Bewegungsrichtungen der Kugeln eine Enumeration mit allen möglichen Richtungen.

   1: public enum Direction {    
   2:     UpperLeft,    
   3:     UpperRight,    
   4:     Left,    
   5:     Right,    
   6:     LowerLeft,    
   7:     LowerRight    
   8: }  

Das Problem daran ist, dass ich zu jeder Richtung wissen muss, ob die x/y Koordinate hoch oder runter gezählt werden muss oder ob sie gleich bleibt. Desweiteren muss ich zu einer Richtung die Gegenrichtung kennen.

Der erste Ansatz war eine Methode der den inkrementellen Wert für die Richtung über ein switch-case zurück gibt und ein Dictionary, dass als Schlüssel eine Direction hat und als Value die entsprechende Gegenrichtung.

Viel schöner finde ich es, dass über die Enumeration-Klasse zu machen.

Hier die neue Implementierung der Direction:


   1: public class Direction : Enumeration {    
   2:     private readonly Func<Direction> inverseProjection;    
   3:     public int Xincrement { get; private set; }    
   4:     public int Yincrement { get; private set; }    
   5:      
   6:     public Direction InverseDirection { get { return inverseProjection(); } }    
   7:      
   8:     public Direction(string displayName, int xincrement, int yincrement, Func<Direction> inverseProjection)    
   9:         : base(displayName) {    
  10:         this.inverseProjection = inverseProjection;    
  11:         Xincrement = xincrement;    
  12:         Yincrement = yincrement;    
  13:     }    
  14:      
  15:     public static readonly Direction UpperLeft = new Direction("UpperLeft",-1,-1, () => LowerRight);    
  16:     public static readonly Direction UpperRight = new Direction("UpperRight",0,-1, () => LowerLeft);    
  17:     public static readonly Direction Left = new Direction("Left", -1,0, () => Right);    
  18:     public static readonly Direction Right = new Direction("Right", 1, 0, () => Left);    
  19:     public static readonly Direction LowerLeft = new Direction("LowerLeft", 0, 1, () => UpperRight);    
  20:     public static readonly Direction LowerRight = new Direction("LowerRight", 1, 1, () => UpperLeft);    
  21: }


Wie man sehen kann, wurde der Klasse X und Y Inkrement hinzugefügt. Die Erweiterung ist nicht sehr aufregend, da ich einfach im Konstruktor die entsprechende Konstante übergebe.

Viel interessanter ist die InverseDirection. Im ersten Versuch habe ich einfach im Konstruktor die entsprechende Gegenrichtung übergeben. Leider legt sich das Programm direkt auf die Nase, da zum Zeitpunkt der Zuweisung die Gegenrichtung noch NULL ist. Deshalb wird hier die Lambda Expression verwendet.

Was man noch refactoren könnte ist, dass im Konstruktor keine nackten Zahlen mehr übergeben werden. Das Problem ist, dass nach mehreren Monaten keiner mehr weiß was “-1” bedeutet.

Das schöne ist, dass ich meine switch-case Anweisung und das Dictionary nun wegwerfen kann und der Code wesentlich sauberer aussieht. Im Gegensatz zu


   1: private Ball GetNextBall(Direction dir, int posx, int posy) {    
   2:     int xinc, yinc;    
   3:     GetIncrement(dir, out xinc, out yinc);    
   4:     if (IsOutOfField(posx + xinc, posy + yinc)) {    
   5:         return null;    
   6:     }    
   7:     return GetBall(posx + xinc, posy + xinc);    
   8: }  
finde ich

   1: private Ball GetNextBall(Direction dir, int posx, int posy) {    
   2:     if (IsOutOfField(posx + dir.Xincrement, posy + dir.Yincrement)) {    
   3:         return null;    
   4:     }    
   5:     return GetBall(posx + dir.Xincrement, posy + dir.Yincrement);    
   6: }
wesentlich besser. Wobei GetIncrement ungefähr so aussah:

   1: private void GetIncrement(Direction dir, out int xinc, out int yinc){    
   2:     switch(dir){    
   3:         case Direction.UpperLeft:    
   4:             xinc = -1;    
   5:             yinc = -1;    
   6:             break;    
   7: ...