Terminal-Testklasse
Terminal.java
Moritz Halm hat eine einfache Klasse geschrieben, die dazu verwendet werden kann, Programme, die die Terminal-Klasse des Lehrstuhls für ihre Ein- und Ausgabe nutzen, zu testen. Dabei erlaubt sie es, die Matcher der Hamcrest-Bibliothek zu verwenden, wodurch sie trotz ihrer Einfachheit sehr flexibel ist.
TL;DR: Beispielprojekt
Wer sich direkt am Code ansehen will, wie das Ganze funktioniert, kann sich einfach das Beispielprojekt herunterladen und in Eclipse importieren. Das Projekt kann auch gut als Vorlage für das eigene Projekt dienen.
Einrichtung
Statt der Terminal
-Klasse der Vorlesung wird unsere Klasse Terminal.java in das Projekt gelegt. Wenn sie nicht in den Test-Modus versetzt wird, verhält sich die Klasse genau wie die Terminal-Klasse des SDQs. Somit kann auch weiterhin im Terminal mit dem Programm interagiert werden.
JUnit und Hamcrest
Um JUnit zum Testen zu verwenden, wird wie üblich JUnit zum Eclipse-Projekt hinzugefügt:
- Rechtsklick auf das Projekt, im Kontextmenü und dann auswählen
- im Reiter auf drücken, auswählen und auf und drücken
Die Testklasse wird erst dadurch mächtig, dass man die Matcher-Bibliothek Hamcrest verwendet, um die erwartete Ausgabe zu spezifizieren. Dieses wird wie folgt in’s Eclipse-Projekt eingebunden:
- hamcrest-all-1.3.jar herunterladen und zum Beispiel im Unterordner
lib
ablegen - Rechtsklick auf das Projekt, im Kontextmenü und dann auswählen
- im Reiter
hamcrest-all-1.3.jar
auswählen auf klicken und die heruntergeladene - im Reiter auswählen und mit nach oben verschieben (muss oberhalb von JUnit sein!)
Eine gute Einführung zu Hamcrests Matchern gibt es von Vogella, eine detaillierte Anleitung um eigene Matcher zu schreiben, gibt es bei planetgeek.ch. Um alle Hamcrest-Matcher auf einmal zu importieren, verwendet man am besten diesen statischen Import:
import static org.hamcrest.Matchers.*;
Optional: Quelltext und Javadoc von Hamcrest mit einbinden
Damit Eclipse die Dokumentation der Hamcrest-Bibliothek anzeigt, muss deren Quelltext eingebunden werden:
- hamcrest-all-1.3-sources.jar herunterladen und zum Beispiel im Unterordner
lib
ablegen - Rechtsklick auf das Projekt, im Kontextmenü und dann auswählen
- im Reiter auf ausklappen und auswählen (Doppelklick)
- im Reiter
hamcrest-all-1.3-sources.jar
auswählen (über )
Verwendung
Ein kleines Beispiel dafür, wie die Testklasse verwendet werden kann, ist in der Klasse ExampleTest
zu finden.
Testmodus anschalten
Um mit der Klasse testen zu können, muss sie erst in den Test-Modus versetzt werden. Das geht am besten in einer mit @BeforeClass
annotierten Methode:
@BeforeClass
public static void enableTerminalTestingMode() {
Terminal.enableTestingMode();
}
Ein- und Ausgabe spezifizieren
Nun wird Befehl für Befehl die Eingabe spezifiziert und dabei angegeben, welche Ausgabe der Befehl haben soll. Hierzu stehen diese Methoden zur Verfügung:
#addMultipleLineOutputThatMatches(String, Matcher<? super List<String>>)
Für ein Befehl, der mehrere Zeilen ausgeben kann. Als zweites Argument wird ein Matcher für die Liste an ausgegebenen Zeilen angegeben.
#addMultipleLinesOutputThatIsExactly(String, String... lines)
Für einen Befehl, der mehrere Zeilen ausgeben kann. Als zweites Argument wird genau angegeben, welche Zeilen erwartet werden.
#addSingleLineOutputThatMatches(String, Matcher<String>)
Für einen Befehl der genau eine Zeile ausgeben soll. Als zweites Argument wird ein Matcher für diese Zeile angegeben.
#addSingleLineOutputThatIsExactly(String, String)
Für einen Befehl der genau die im zweiten Argument angegebene Zeile ausgeben soll.
#addNoOutput(String)
Für einen Befehl, der keine Ausgabe haben soll.
Aufräumen
Nach einem Durchlauf muss die Ausgabe des letzten Befehls überprüft und dann das Terminal wieder zurück auf Anfang gesetzt werden. Das geht am einfachsten in einer mit @After
annotierten Methode:
@After
public void cleanUp() {
Terminal.flush();
Terminal.reset();
}
Dank
Die Idee für diese einfache Testklasse stammt von Moritz Halm. Ich möchte ihm dafür danken, dass er sie hier zur Verfügung stellt. Niels Philipp Modry hat eine einfach Verbesserung der Klasse zur Verfügung gestellt, die es erlaubt, mehrere Zeilen auch mit nur einen Aufruf an Terminal#printLine
auszugeben. Dafür möchte ich auch ihm danken.
Fortgeschrittenes
Die Testklasse ist trotz ihrer Einfachheit sehr mächtig. Es sollte damit möglich sein, alle Anforderungen abzubilden, die an Programme im Rahmen von Aufgaben der Vorlesung gestellt werden. Trotzdem gibt es ein paar Einschränkungen:
- Die Tests rufen die
main
-Methode direkt auf. Sollte das Programm Zustand in statischen Attributen speichern, so wird der Test nicht dem echten Verhalten des Programms entsprechen, da diese nicht zurückgesetzt werden. Die Lösung ist, keinen Zustand in statischen Attributen zu halten (ohnehin eine gute Idee!) oder die Variablen bei jedem Aufruf vonmain
zurückzusetzen. - Die Fehlermeldungen der Tests sind ein wenig kompliziert und irreführend, da die Matcher Listen von Strings beschreiben.
Ich habe im ersten Semester eine Test-Bibliothek geschrieben, das diese Probleme angeht: Jede Ausführung erfolgt in einem eigenen ClassLoader
und eigene Matcher sorgen für detaillierte Fehlermeldungen. Die Bibliothek schießt allerdings eher mit Kanonen auf Spatzen. Sollte sich trotzdem jemand dafür interessieren, findet man sie hier: git.joshuagleitze.de/progtutors/submissiontest.