Eine der Aufgaben, die Programmierer am wenigstens mögen, ist das Testen. Die Ausreden sind vielfältig:
- Ich habe keine Zeit zum Testen.
- Testen ist stupide und langweilig.
- Mein Code ist praktisch fehlerfrei.
- Mein Code ist gut genug.
- usw.
Leider ist Testen ein sehr wichtiger Vorgang, denn nur so kann man sicherstellen, dass der Kunde ein Programm erhält, bei dem keine - eventuell teuren - Nachbesserungen notwendig sind. Die Tatsache, dass die meisten Entwickler dieser wichtigen Tätigkeit nur ungerne nachgehen, haben auch Erich Gamma und Kent Beck erkannt. Daraufhin haben sie JUnit entwickelt.
JUnit ist ein kleines aber mächtiges Framework für Java-Programme. Es ermöglicht das Schreiben und Ausführen von automatischen Tests. Die mit JUnit entwickelten Testfälle sind selbstüberprüfend und damit wiederholbar. Somit entfällt das ständige Eingeben von Testdaten.
Die Tests werden definiert und anschließend können sie immer wieder gestartet werden. Im Idealfall werden Tests nach jedem Kompilieren durchgeführt. Was natürlich in der Praxis nicht getan wird. Mit JUnit ist sogar das möglich, da die Tests ja bereits definiert sind und nur noch gestartet werden müssen.
Download und Installation von JUnit
Die aktuelle Version von JUnit ist 4.12. In der Version 4 hat es gegenüber der Version 3 einige große Änderungen gegeben. So ist das Framework z.B. komplett auf die Annotationen umgestellt worden, die es seit Java 1.5 gibt. Herunterladen können Sie das Framework von der Projektseite.
JUnit befindet sich in einem jar-Archiv. Darin finden Sie auch die Quelltexte, die Dokumentation und weitere interessante Dokumente. Kopieren Sie die datei junit.jar in Ihren Classpath und schon ist das Framework einsatzbereit.
Test first
Eine empfehlenswerte Vorgehensweise ist test first. Das bedeutet, dass die Tests vor der eigentlichen Anwendung entwickelt werden. Der Code soll möglichst einfach zu testen sein. Das hat den Vorteil, dass überschnelle Entwickler, die erst programmieren und dann denken, erst einmal über den Code nachdenken müssen. Sonst müssen sie unter Umständen nach einiger Zeit wieder alles ändern, was für Chaos sorgt und Zeit und Geld kostet. Bei konsequent durchgeführtem test first hat man bereits vor der Entwicklung eine gute Vorstellung von den Klassen und spätere Änderungen sind nicht mehr so häufig notwendig. Außerdem ist eine Klasse, die gut zu testen ist, auch gut zu benutzen. Immerhin wird sie bei den Tests ja benutzt.
Wenn sich der Entwickler bei jeder Klasse sofort überlegt, wie sie am besten zu testen ist, gibt es am Ende keinen Code, der unmöglich zu testen ist. Und da Tests bei der Minimierung von Fehlern helfen sollen, ist das eigentlich im Sinne aller Beteiligten.
Aber wie sollte man nun genau vorgehen? Die nächste Auflistung kann als Anregung für ein gutes Vorgehen betrachtet werden:
- Klassen und Methoden festlegen, gerne auch mit UML-Klassendiagramm.
- Klasse und Methoden soweit anlegen, dass Kompilierung möglich ist (ohne Logik).
- API-Dokumentation schreiben
- Wenn sich die Klasse nicht "richtig" anfühlt, wird sie ab Schritt 1 noch mal geändert.
- Eine Testklasse implementieren.
- Nun wird die Logik der Methoden implementiert.
- Wenn sich durch die Implementierung neuer zu testender Code ergeben hat, werden die Testfälle erweitert.
- Tests durchführen und Fehler beheben.
Das klingt auf den ersten Blick nach mehr Arbeit. Allerdings hat sich gezeigt, dass das nur im ersten Moment so ist. Die Investition am Anfang zahlt sich aus, da der Code später fehlerfreier und vor allem besser wartbar ist.
Wann sollen Tests durchgeführt werden? So oft wie möglich! Je eher ein Test durch einen Fehler fehlschlägt, desto eher kann der Fehler behoben und Folgefehler können verhindert werden. Es gilt also: Nach größeren Änderungen wird das gesamte Programm getestet! Und wenn dies nicht automatisch geschieht, geht hier jede Menge Zeit verloren. Folge: Es wird nicht getestet!
Testfälle
Mit JUnit können Sie sich Testfälle definieren. In diesen rufen Sie Methoden auf und lassen JUnit überprüfen, ob die Methoden bestimmte erwartete Ergebniswerte produzieren. Dabei testen Sie wie mit einer Benutzeroberfläche. Dort geben Sie auch bestimmte Testwerte ein und prüfen, ob Sie das erwartete Ergebnis erhalten.
Sie haben z.B. eine Methode add(int wert1, int wert2) geschrieben. Sie können die Methode testen, indem Sie sie folgendermaßen aufrufen: add(5, 9) und kontrollieren, ob Sie den Wert 14 erhalten. Erhalten Sie ein anderes Ergebnis, ist die Methode falsch.
Genauso arbeitet JUnit. Mit einem einzigen Unterschied: Sie geben die Testwerte nicht jedes Mal neu ein, sondern speichern die Testfälle an. Mal angenommen, unsere Methode add() erzeugt das Ergebnis 10. Eindeutig falsch. Nun geht an die Korrektur. Anschließend testen wir die Methode noch einmal mit zwei Werten. Wieder ein falsches Ergebnis. Irgendwann läuft unser Test. Aber wir haben mehrmals Testdaten eingeben müssen. Bei einer Methode zum Addieren ist der Aufwand noch überschaubar, bei größeren Programmen kann dies jedoch schnell nerven. Mit JUnit jedoch erzeugen Sie die Testfälle genau einmal, speichern sie ab und starten sie einfach. Sie erhalten die Testergebnisse und können die Fehler korrigieren. Der Testaufwand ist wesentlich geringer.
Das klingt ja toll! Gibt es so etwas auch für andere Programmiersprachen?
Auch für andere Sprachen gibt es ähnliche Frameworks, da die Notwendigkeit richtigen Testens nicht nur für Java erkannt wurde. Hier eine kleine Auflistung.
- Java:
- JUnit
- TestNG
- C#:
- NUnit
- NCover
- PartCover
- Delphi: DUnit
- PHP:
- PHPUnit
- SimpleTest
- ApacheTest
- C++:
- API Sanity Autotest
- ATF
- CppUnit
Diese Liste legt keinerlei Anspruch auf Vollständigkeit, sondern soll nur einen kleinen Eindruck von der Vielzahl verschiedener Testframeworks und damit auch der Notwendigkeit solcher Hilfsmittel geben. Auch für Java gibt es andere Test-Frameworks als JUnit wie z.B. TestNG
... link (1 Kommentar) ... comment