JUnitloop: Test-driven Development meets Instant Feedback

JLoop – Instant Feedback für Java

Während der Entwicklung von JUnitLoop wurde schnell klar, dass das automatische Ausführen von Code beim Speichern noch mehr Anwendungsfälle bietet. Betrachtet man beispielsweise die Erstellung eines komplexen, dynamischen User Interface (UI), für das man keinen WYSIWYG-Editor nutzen kann, so führt man mehr oder minder oft die folgenden Schritte aus:

  • „Run as Java application“
  • Ergebnis optisch prüfen
  • Erzeugtes Fenster schließen
  • Code zum Erzeugen des UI ändern

Oft werden bei jeder Iteration dieser Schritte nur wenige Zeilen Code geändert. Ein ähnliches Vorgehen kann man auch beim programmatischen Erzeugen einer Szene mit Java 3D beobachten. Da man die Auswirkungen bestimmter API-Aufrufe auf das Endergebnis oft schwer abschätzen kann, bleibt einem nichts anderes übrig, als verschiedene Varianten auszuprobieren und das gerenderte Ergebnis manuell zu prüfen.

Die Erstellung von dynamischen 2-D- oder 3-D-UIs erfordert oft dieses Trial-and-Error-Vorgehen. Und obwohl diese Methode zugegebenermaßen nicht optimal ist, so stellt sie doch nicht selten den einzig gangbaren Weg dar. Im Allgemeinen tritt dieser Effekt meistens auf, wenn man gezwungen ist, gegen ein unbekanntes, komplexes API zu programmieren.

Um Entwickler bei diesem Programmierstil zu unterstützen und den RUN-Button zu schonen, wurde JLoop, die kleine Schwester von JUnitLoop, entwickelt. JLoop ist ähnlich minimalistisch wie JUnitLoop, führt aber Klassen statt Tests aus. Um JLoop zu nutzen, braucht man eine Klasse, die eine run()-Methode ohne Argumente enthält. Hat man so eine Klasse erstellt, kann man per Kontextmenü (Abb. 2) die Option RUN IN LOOP aktivieren. Damit überwacht JLoop alle Änderungen an der Klasse und ihren Abhängigkeiten. Nach jeder relevanten Änderung wird die Klasse neu instanziiert und die run()-Methode aufgerufen. Optional wird vorher die Instanz der Klasse, die beim vorherigen Durchlauf erzeugt wurde, von JLoop zerstört, indem die stop()-Methode aufgerufen wird, falls sie existiert.

Abb. 2: Neuer Menüpunkt zum Aktivieren von JLoop

Um die Nutzung von JLoop anhand der Entwicklung einer kleinen SWT-Oberfläche zu verdeutlichen, sei hier auf Listing 1 verwiesen. Fügt man beispielsweise die Anweisungen in der run()-Methode sukzessive Zeile für Zeile hinzu und speichert nach jeder Zeile, kann man beobachten, wie das GUI Schritt für Schritt erweitert wird und neue Elemente erscheinen. In Abbildung 3 ist das Ergebnis für das Speichern nach den einzelnen Iterationen dargestellt.

Listing 1
package de.devboost.jloopdemo;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

public class JPhone {

  private Display display;

  public void run() {
    display = new Display();
    Shell shell = new Shell(display);
    shell.setText("jPhone");
    shell.setLayout(new GridLayout(3, false));

    // first iteration
    Text numberView = new Text(shell, SWT.BORDER | SWT.NO_FOCUS );
    numberView.setText("+49");
    numberView.setEditable(false);
     
    // second iteration
    GridData numberViewGridData = new GridData(SWT.HORIZONTAL );
    numberViewGridData.horizontalSpan = 3;
    numberViewGridData.grabExcessHorizontalSpace = true;
    numberView.setLayoutData(numberViewGridData);
   
    // third iteration
    for (Integer i = 1; i 

Abb. 3: SWT User Interface jeweils nach dem iterativen Hinzufügen des UI-Codes

Um zu verhindern, dass nach jedem Speichern der Klasse MyForm ein neues Fenster erscheint und man irgendwann nicht mehr weiß, welches das aktuelle Fenster ist, ist die stop()-Methode so implementiert, dass das aktuell geöffnete Fenster durch den Aufruf von dispose()geschlossen wird. Einfach ausgedrückt werden also das Starten der Anwendung und das Schließen des Fensters automatisiert. Die beiden anderen Schritte („Ergebnis optisch prüfen“ und „Code zum Erzeugen der UI ändern“) müssen naturgemäß weiterhin vom Entwickler ausgeführt werden.

Je nach Implementierung der run()-und stop()-Methoden kann man mit JLoop einfach und schnell gegen ein unbekanntes API programmieren und prüfen, was dabei passiert. APIs, die grafische oder Audioausgaben produzieren, sind gute Beispiele für typische Anwendungsfälle. Der Aufruf eines unbekannten Web Service kann aber ebenfalls mit JLoop realisiert werden. Hat man mit JLoop per Trial and Error herausgefunden, welche Aufrufe die relevanten Daten zurückliefern, kann man den entstandenen Code einfach in einen JUnit Test überführen (@Test-Annotation und Assertions hinzufügen).

Neben dem Ausführen von Klassen als normale Java-Applikation beim Speichern kann man JLoop auch dazu anhalten, die Instanziierung der Klasse in Eclipse selbst, d. h. in der aktuellen JVM, statt in einer neuen durchzuführen. Dazu muss man statt run()eine Methode runInSameVM()implementieren. Dadurch wird es möglich, Eclipse Extensions abzufragen oder Informationen geladener Plug-ins abzurufen.

Wie bereits weiter oben erwähnt, ist JLoop weder ein Ersatz für JUnitLoop noch Letzterem vorzuziehen. Wann immer es möglich ist, sollte man versuchen, erwartetes Verhalten mit Unit Tests zu spezifizieren. Nur wenn es sehr gute Gründe dafür gibt, das nicht zu tun, sollte man auf JLoop ausweichen, um das unausweichliche Trial-and-Error-Vorgehen zumindest zu beschleunigen.

Kommentare

Schreibe einen Kommentar

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