Die testgetriebene Entwicklung entfaltet ihre Wirkung am besten, wenn die Ausführungzeit der Tests kurz und das Ergebnis dementsprechend schnell verfügbar ist. Dieses schnelle Feedback ist gefährdet, wenn man für verschiedene Tests eine Ressource benötigt, deren Bereitstellung geraume Zeit (in der Softwareentwicklung gilt das bereits für einen Zeitraum von wenigen Sekunden) in Anspruch nimmt und die darüber hinaus am Ende „entsorgt“ werden muss. Beispiele sind das Einrichten einer Datenbank oder das Starten eines (embedded) Applikationsservers. Da bietet es sich an, diese Ressource für mehrere Tests nur einmal bereitzustellen. Um dies mit JUnit zu realisieren, finden sich in der Literatur und im Netz zwei Vorschläge:
- Alle Tests, die die Ressource benötigen, werden in einer Suite zusammengefasst. Die Suite wird dann nach der Initialisierung der Ressource aufgerufen und anschließend „aufgeräumt“.
- Man implementiert eine Testklasse, die das Fixture in
@BeforeClass
bereitstellt. Testklassen, die dieses Fixture benutzen wollen, müssen dann von dieser Klasse erben.
Beide Ansätze haben ihre Nachteile: Während der erste Ansatz dazu führt, dass man eine Suite zu pflegen hat, entbehrt der zweite Ansatz eine Möglichkeit, nach mehreren Tests das „kostspielige“ Feature wieder aufzuräumen. Der erste Ansatz hat darüber hinaus zur Folge, dass sich das praktische Eclipse-Feature, alle Tests in einem Ordner oder Package auf einmal durchführen zu lassen, nicht mehr zuverlässig nutzen lässt.
Eine Alternative, die weder auf Vererbung noch auf eine Suite zurückgreift, ist die Implementierung eines TestRunners. Unter JUnit 4.1 sähe das beispielsweise so aus:
public class CustomRunner extends TestClassRunner { private static int startUpRequests; public static ExpensiveResource resource; /** * Der Konstruktor wird dazu benutzt, die Anzahl der Testklassen * festzustellen, die die gemeinsame Ressource benötigen. */ public CustomRunner(Class<?> klass) throws InitializationError { super(klass); startUpRequests++; } /** * Diese Methode wird pro Testklasse einmal aufgerufen. Sie kümmert * sich um die einmalige Erstellung sowie die "Entsorgung" der Ressource. */ @Override public void run(RunNotifier notifier) { initResourceIfNecessary(); super.run(notifier); startUpRequests--; disposeResourceIfNecessary(); } private static void disposeResourceIfNecessary() { if (startUpRequests == 0){ // Entsorgen der Ressource. } } private static void initResourceIfNecessary() { if (ressource == null) { // Initialisieren der Ressource. } } }
CustomRunner
wird abgeleitet vom Standard-TestRunner der jeweiligen JUnit-Version. Unter JUnit 4.8 bietet sich die Klasse BlockJUnit4ClassRunner
an. Die Testklassen müssen jetzt nur noch mit @RunWith(CustomRunner)
annotiert werden.