JUnitloop: Test-driven Development meets Instant Feedback

JUnitLoop – aktivieren und fertig

JUnitLoop ist ein leichtgewichtiges Plug-in für Eclipse, das die o. g. Vision in die Praxis umsetzt. Nach der Installation steht in der Eclipse-eigenen JUnit View der neue Menüpunkt RUN IN LOOP zur Verfügung (Abb. 1). Aktiviert man diesen Menüpunkt, wird JUnitLoop im Hintergrund aktiviert und führt fortan nach jeder Codeänderung alle betroffenen Tests aus. Die Ergebnisse der Testläufe werden dabei wie gewohnt in der JUnit View angezeigt, sodass man leicht zu fehlgeschlagenen Tests navigieren kann.

Abb. 1: Neuer Menüpunkt zum Aktivieren von JUnitLoop

Um die richtigen Tests auszuführen, bestimmt JUnitLoop beim Speichern einer Klasse mithilfe der Java Development Tools (JDT) alle Abhängigkeiten zu eben dieser veränderten Klasse. So ist es möglich, die Menge der Testfälle, die potenziell von der Änderung betroffen sein können, zu bestimmen. Hat JUnitLoop alle diese Tests bestimmt, wird eine spezielle Testsuite erstellt, die die genannten Tests ausführt. Gegenwärtig werden alle Klassen, die vom JDT als JUnit Test erkannt werden, berücksichtigt. Die Testsuite wird in einem separaten Projekt im Workspace mit dem Namen „JUnitLoop“ abgelegt. Dieses Projekt und die darin enthaltenen Testsuiteklassen werden automatisch von JUnitLoop generiert und beim nächsten Testlauf überschrieben.

Um sicherzustellen, dass man nicht vergisst, fehlgeschlagene Tests vor dem Einchecken zu reparieren, führt JUnitLoop nicht nur die Testfälle aus, die von der letzten Änderung betroffen sind, sondern auch alle Testfälle, die zuvor fehlgeschlagen sind. Sobald ein Test allerdings wieder einmal erfolgreich durchlaufen wurde, wird er erst wieder ausgeführt, wenn sich eine Klasse ändert, von der der Test abhängt.

Obwohl die Benutzung von JUnitLoop theoretisch mit der Aktivierung von RUN IN LOOP vollständig erklärt ist, gibt es doch ein paar kleine Besonderheiten, auf die an dieser Stelle hingewiesen werden soll. So ist es beispielsweise möglich, dass Testfälle, die bisher fehlerfrei durchliefen, beim Start mit JUnitLoop fehlschlagen. Die Ursache dafür ist schlichtweg die Tatsache, dass JUnitLoop alle Tests mithilfe einer eigenen Testsuite in einem separaten Eclipse-Projekt startet. Daraus ergeben sich zwei Unterschiede im Vergleich zur üblichen Ausführung der Tests:

  1. Das Arbeitsverzeichnis der Tests unterscheidet sich
  2. Der Klassenpfad enthält u. U. mehr Projekte, als für die Ausführung eines einzelnen Tests nötig ist

Verwendet man Tests, die vom aktuellen Arbeitsverzeichnis abhängen, können diese aufgrund des ersten Unterschieds bei der Ausführung mit JUnitLoop fehlschlagen. Ein typisches Symptom dafür ist beispielsweise, dass Ressourcen mit Testeingabedaten nicht gefunden werden. Im Allgemeinen ist es leicht möglich, diese Tests so anzupassen, dass sie unabhängig vom Arbeitsverzeichnis funktionieren, indem man benötigte Ressourcen z. B. über den ClassLoader der Testklasse aus dem Test-Package bezieht.

Der zweite Unterschied, nämlich die Tatsache, dass JUnitLoop mit einem erweiterten Klassenpfad arbeitet, ergibt sich daraus, dass die Testsuite Tests aus verschiedenen Projekten enthalten kann, die aber keine direkten Abhängigkeiten zueinander haben. Der Klassenpfad für die JUnitLoop TestSuite enthält dann Klassen, die den einzelnen Tests u. U. sonst nicht zugänglich sind. Laden Tests gleichnamige Ressourcen aus dem Klassenpfad, kann es leicht passieren, dass eine falsche Ressource geladen wird, weil sie als Erstes im Pfad auftaucht. Ein Beispiel für diesen Fall ergibt sich u. a., wenn man mehrere Hibernate-Konfigurationen in verschiedenen Projekten direkt im src Folder ablegt.

Um solche Situationen zu vermeiden, sollten Überlappungen von gleichnamigen Dateien in verschiedenen Projekten vermieden werden. Dazu sollte man Ressourcen, z. B. Konfigurations-Files, in einem Unterpackage (z. B. com.mycompany.project_xyz) ablegen. Diese Änderung trägt generell dazu bei, Probleme zu vermeiden, die auftauchen würden, wenn verschiedene Projekte mit gleichnamigen Ressourcen im gleichen Klassenpfad liegen. Das „überlappungsfreie“ Ablegen von Ressourcen ist daher nicht nur im Kontext von JUnitLoop sinnvoll.

Beachtet man die eben genannten Hinweise, so bietet JUnitLoop eine völlig neue Erfahrung beim testgetriebenen Entwickeln. Ein schnelleres Feedback bzgl. der Frage, ob eine geänderte Klasse noch das geforderte Verhalten zeigt, ist (fast) nicht möglich. Sowohl beim Editieren eines JUnit-Tests als auch beim Modifizieren beliebiger anderer Klassen erfährt man sofort, ob eine Änderung existierende Anforderungen verletzt, oder auch, ob ein kurz zuvor hinzugefügter Test nun erfüllt wird.

Obwohl heute in vielen Projekten ein CI-System eine ähnliche Aufgabe wie JUnitLoop übernimmt, ergänzen sich das sofortige Feedback in der IDE und die langfristige Absicherung mit Testfällen während des Builds gegenseitig. Zum einen verhindert JUnitLoop viele Commits, die den Build fehlschlagen lassen würden, zum anderen stellt die Ausführung von Tests auf dem CI-System sicher, dass alle Tests ausgeführt werden und nicht nur die im Workspace des Entwicklers. Darüber hinaus können langlaufende Tests (z. B. komplexe Integrationstests) in der IDE außen vor gelassen werden.

Die häufige Ausführung von Tests, wie sie durch JUnitLoop forciert wird, hat noch weitere positive Konsequenzen: Der Wert der Testklassen steigt sofort, denn mit jeder Ausführung werden die im Test kodierten Annahmen überprüft. Fehler werden schon direkt bei der Entstehung erkannt. Da die Kosten für die Beseitigung von Fehlern bekanntlich mit der Dauer ihrer Existenz steigen, kann JUnitLoop direkt dazu beitragen, effizienter zu entwickeln.

Über den Wert der Tests hinaus steigt die Motivation, schnelle Unit Tests zu erstellen, um nicht lange auf das Testergebnis warten zu müssen. Dadurch verringern sich langfristig auch die Build-Zeiten, bzw. können mehr Tests in der gleichen Zeit ausgeführt werden. Letztlich erlaubt es JUnitLoop, Tests zu nutzen, um das dynamische Verhalten des eigenen Codes auf ähnlich strenge Art und Weise zu prüfen, wie es der Compiler für die Syntax und die statische Semantik realisiert. Aus unserer Sicht ist das der nächste logische Schritt hin zur effizienten Entwicklung hochwertiger Software.

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.