Gwen: BDD-Framework für lesbare und refaktorisierbare Tests in Java

Seite 2: BDD mit Gwen

Inhaltsverzeichnis

Die Entwickler des durch seinen Musikidentifikationsdienst bekannten Unternehmens Shazam Entertainment haben sich des Problems angenommen und das Micro-Framework Gwen erstellt. Mit ihm lassen sich in Java Testfälle im Given/When/Then-Schema schreiben. Durch die Typsicherheit sind Content-Assist und Refactoring in jeder modernen IDE möglich. Gwen ergänzt andere Test-Frameworks und lässt sich einfach in bestehende Tests (zum Beispiel JUnit und TestNG) integrieren.

Im einfachsten Fall fügt man der Testklasse drei statische Imports Given/When/Then hinzu. Diesen Methoden ist zudem eine Referenz für das zu testende System zu übergeben. Ein JUnit-Test im BDD-Stil kann mit Gwen wie folgt aussehen (alle Quellen stehen im Übrigen auf GitHub zur Verfügung):

import static com.shazam.gwen.Gwen.given;
import static com.shazam.gwen.Gwen.when;
import static com.shazam.gwen.Gwen.then;

/**
* In Order to avoid stupid mistakes
* As a cautious person
* I use a calculator.
*/
public class FeatureCalculator {

// The system under test (SUT)
private CalculatorSUT theCalculator =
new CalculatorSUT();

@Test
public void scenarioSimpleAddition() {
given(theCalculator).isTurnedOn();
when(theCalculator).adds(4).adds(5);
then(theCalculator).showsAsResult(9);
}

}

Vergleicht man das Format der Klasse mit dem ursprünglichen Feature im Gherkin-Format, erscheint die Klasse ähnlich lesbar. Bei der Formulierung der Schritte lassen sich Parameter nur am Ende als Methodenparameter übergeben. Um die Lesbarkeit zu erhalten, sollten Methoden idealerweise einen Parameter besitzen. Komplexe Übergabeparameter könnten Entwickler zum Beispiel via Builder-Pattern initialisieren.

Die drei statischen Methoden bauen darauf auf, dass die Klasse CalculatorSUT drei Methoden anbietet, die jeweils eine Instanz einer weiteren Klasse zurückliefern. Diese Klassen implementieren die Methoden, die für die Given/When/Then-Schritte des Szenarios vorgesehen sind. Eine Arranger-Klasse ist für das Bereitstellen aller Methoden rund um Given zuständig, die Actor-Klasse für die um When und eine Asserter-Klasse für die um Then:

import com.shazam.gwen.collaborators.Actor;
import com.shazam.gwen.collaborators.Arranger;
import com.shazam.gwen.collaborators.Asserter;
import com.shazam.gwen.gwt.Given;
import com.shazam.gwen.gwt.Then;
import com.shazam.gwen.gwt.When;

import static org.fest.assertions.Assertions.assertThat;

public class CalculatorSUT implements
Given<CalculatorSUT.CalculatorArranger>,
When<CalculatorSUT.CalculatorActor>,
Then<CalculatorSUT.CalculatorAsserter> {

private Calculator device;

private CalculatorArranger arranger;
private CalculatorActor actor;
private CalculatorAsserter asserter;

public CalculatorSUT() {
device = new Calculator();
arranger = new CalculatorArranger();
actor = new CalculatorActor();
asserter = new CalculatorAsserter();
}

public CalculatorArranger given() {
return arranger;
}

public CalculatorActor when() {
return actor;
}

public CalculatorAsserter then() {
return asserter;
}

public class CalculatorArranger implements Arranger {

public void isTurnedOn() {
device = new Calculator();
}
}

public class CalculatorActor implements Actor {

public CalculatorActor adds(long val) {
device.add(val);
return this;
}

public CalculatorActor multipliesBy(long val) {
device.multiplyBy(val);
return this;
}

public CalculatorActor powerBy(long val) {
device.power(val);
return this;
}

}

public class CalculatorAsserter implements Asserter {

public void showsAsResult(long val) {
assertThat(device.getState()).isEqualTo(val);
}

}
}

Durch das Aufteilen der Schritte in die unterschiedlichen Klassen kann die IDE die Methoden passend zur getesteten Systemkomponente und zur Art des Schritts zur Vervollständigung anzeigen (Abb. 2). Auch ein Springen vom Szenario in die Implementierung ist möglich. Im Gegensatz zu Gherkin lässt sich in Java eine Methode mit Refactoring umbenennen und von einer Methode in das Szenario zurückspringen.

Content-Assist für Java in IDEA IntelliJ (Abb. 2)

Fügt man statt einer bestehenden eine neue Methode ein, zeigt der Compiler der IDE mit einer roten Markierung einen Fehler an. Über einen Hotkey generiert die IDE auf Wunsch einen leeren Methodenrumpf in der passenden Klasse (je nach Kontext im Arranger, Actor oder Asserter).