Testen mit JUnit · 1 Einleitung oder „Was ist JUnit“ Wenn man bei Wikipedia unter Junit schaut erhält man folgende Definition: „Junit, (Jwnyt), auch Init, eine altägyptische
Post on 15-Jul-2020
7 Views
Preview:
Transcript
Testen mit JUnit
Softwareengineering
Verfasser:
Ronny Boost, Michael Künne
BA Horb
Studiengang: IT
Jahrgang: 2003
Ronny Boost - 2 - 21.12.2005
Michael Künne
Inhaltsverzeichnis
1 Einleitung oder „Was ist JUnit“ ............................................................................ 3
2 Testarten ............................................................................................................. 4
2.1 Unit-Test....................................................................................................... 5
2.2 Integrationstest............................................................................................. 6
2.3 Blackbox-Test .............................................................................................. 6
2.4 Whitebox-Test .............................................................................................. 7
2.5 Zusammenfassung....................................................................................... 8
3 Testen mit JUnit .................................................................................................. 9
3.1 JUnit ............................................................................................................. 9
3.2 Installation ...................................................................................................12
3.3 Beispiel für einen Test.................................................................................12
3.4 Test Driven Development............................................................................15
3.4.1 Vorteile des TDD..................................................................................17
3.4.2 Nachteile des TDD...............................................................................17
3.5 konventioneller Softwareerstellung vs. TDD................................................18
4 Zusammenfassung.............................................................................................19
5 JUnit Erweiterungen ...........................................................................................20
6 Abbildungsverzeichnis........................................................................................21
7 Quellen ...............................................................................................................22
1 Einleitung oder „Was ist JUnit“
Wenn man bei Wikipedia unter Junit schaut erhält
man folgende Definition: „Junit, (Jwnyt), auch Init,
eine altägyptische Göttin im Amduat (Totenbuch),
die farbige.“
Abbildung 1.1: altägyptischeGöttin Junit
Wenn man hingegen nach JUnit sucht erfährt man, dass es sich hierbei um „ein
Framework zum Testen von Java-Programmen“ handelt. Hierbei liegt besonderes
Augenmerk auf automatisierten Unit-Tests einzelner Klassen. Beim Testen im
Allgemeinen handelt es sich nicht um die fälschliche Annahme, zu verifizieren, dass
die Anwendung korrekt funktioniert, sondern um das Lokalisieren von Logikfehlern,
welche ausschließlich zur Laufzeit auftreten. Bei Unit-Tests, auch Komponententests
genannt, im Speziellen geht es um die Validierung einzelner Module einer Software,
zum Beispiel einzelner Klassen. Dass das Testen besonders in der
Softwareentwicklung einen sehr hohen Stellenwert haben sollte, sieht man bereits an
folgenden Zahlen [6]:
• 50-60 gefundene Fehler pro 1000 LOC während der Entwicklung
• Bis zu 3 Fehler pro 1000 LOC nach der Auslieferung
• Bei sicherheitskritischen Anwendungen ca. 0.5 Fehler pro 1000 LOC
• OB Wahl in Neu-Ulm 1995: 104% Wahlbeteiligung (tatsächlich 52%)
• Bluescreen
• Gepäckverteilungsanlage Flughafen von Denver: 16 Monate Verzögerung,
Schaden 550Mio USD
• Golfkrieg 1991: 28 Tote, fast 100 Verletzte wg. fehlerhafter
Uhrsynchronisation
Ronny Boost - 3 - 21.12.2005
Michael Künne
Ronny Boost - 4 - 21.12.2005
Michael Künne
2 Testarten
Ein Softwaretest bezeichnet in der Informatik ein mögliches Verfahren zur teilweisen
Verifikation und Validierung eines Programms. Der Test dient der Qualitätssicherung
einer neuen oder geänderten Software. Hierbei ist das Ziel das reelle Verhalten mit
Hilfe von Testfällen zu untersuchen und mit den erwarteten Ergebnissen zu
vergleichen und zu dokumentieren. Hierbei geht es um eine Aktivität der
Softwareentwicklung.
Natürlich gibt es nicht nur eine Art von Test mit der man alle Problemstellungen
abdecken kann.
Name Beschreibung
Application-Level-Test Test der gesamten Anwendung aus Benutzersicht in
einer Testumgebung.
(Öffentlicher) Betatest Testen einer Anwendung in produktiver Umgebung.
Performance-Test Testet das Verhalten der Anwendung unter
verschiedener Last (etwa Antwortzeiten,
Speicherverbrauch).
Skalierbarkeits-Test Ähnlich dem Performance-Test, mit Schwerpunkt auf
Veränderung der Leistung und
Ressourcenbeanspruchung bei der Variation einzelner
Parameter (zum Beispiel Zugriffe auf eine Datenbank).
Unit-Test Test der Bausteine einer Software (je nach
Programmiersprache zum Beispiel Klassen oder
Unterprogramme).
White-Box-Test Testen von Code durch möglichst umfassend
abgedeckte Parameterbereiche mit dem Ziel,
potenzielle Laufzeitfehler zu ermitteln (White-Box, da
Kenntnisse der internen Struktur für die
Parametererzeugung genutzt werden).
Black-Box-Test Testen von Code-Einheiten über spezielle Testfälle.
Die Grenze zum White-Box-Test ist fließend, aber es
steht mehr die richtige Verarbeitung von Eingaben im
Ronny Boost - 5 - 21.12.2005
Michael Künne
Mittelpunkt, weniger das Auffinden von Laufzeitfehlern.
Regression-Test Vergleich von Testläufen vor und nach der Änderung
von Code. Ergebnisse sollten sich nur geplant (etwa
als Folge eines Bugfixes) ändern.
Integration-Test Bezeichnet eine aufeinander abgestimmte Reihe von
Einzeltests, die dazu dient, welche dazu dient das
Zusammenspiel mehrerer von einander abhängiger
Komponenten zu testen. Tabelle 2-1: Zusammenfassung verschiedener Testmethoden.
2.1 Unit-Test
Unit-Tests sind Teil einer Softwareentwicklungsvorgehensweise, wie zum Beispiel
„Extreme Programming“. Mit ihrer Hilfe überprüft man die Korrektheit von Modulen
einer Software. Ein wichtiges Anwendungsgebiet ist an dieser Stelle das Refactoring.
Bei Refactoring handelt es sich um das Ziel Quellcode lesbarer zu machen. Um
dabei die Funktionalität im vollem Umfang weiterhin gewährleisten zu können
verwendet man Unit-Tests. Des Weiteren ist es ein integraler Bestandteil bei der
testgetriebenen Programmierung. Hier werden die Testfälle meist vor oder während
der eigentlichen Entwicklung der Software geschrieben und gewartet. Dadurch wird
ermöglicht, dass Tests automatisiert und reproduzierbar wiederholt werden können.
Mit Hilfe von Unit-Tests ist eine unmittelbare Rückmeldung für den Programmierer
möglich, ob das Programm den gewünschten Anforderungen entspricht und korrekte
Ergebnisse produziert oder nicht. Komponententests sind ein essentieller Bestandteil
der Qualitätssicherung und Softwareentwicklung.
Unit-Tests sind eine sehr geeignete Vorstufe zu Integrationstests, welche wiederum
dafür geeignet sind um eine Summe von mehreren Klassen in Abhängigkeit von
einander zu testen. Jedoch werden diese Integrationstests meist manuell
durchgeführt.
Ronny Boost - 6 - 21.12.2005
Michael Künne
2.2 Integrationstest
Da Software nur in den seltensten Fällen aus nur einer Methode besteht, ist es nötig
diese in Kombination miteinander zu testen um zu überprüfen, ob diese im
Zusammenspiel miteinander auch korrekt arbeiten. Hierfür werden Testszenarios
definiert, welche in der Lage sind nachzuweisen, dass die zwei oder mehr Methoden
korrekt und gemäß den Spezifikationen miteinander agieren. Als Methoden werden
hierfür meist Funktions- und Schnittstellentests verwendet. Da die Funktionstests
meist schon in den Unit-Tests verwendet werden, dienen sie an dieser Stelle zum
Überprüfen, ob die korrekten Komponenten verwendet werden. Die
Schnittstellentests werden wiederum dazu verwendet zum Überprüfen, ob zwischen
Komponenten verwendete Daten richtig sind.
Integrationstests sind jedoch nicht auf das Gesamtsystem festgelegt. Da mit der
Größe der Software auch der Testaufwand überproportional steigt, wird die Software
meist in einzelne Untersysteme eingeteilt, die einzeln getestet werden, welche im
Gesamtsystem auch wiederum nur als Komponente gesehen werden.
2.3 Blackbox-Test
Dabei betrachtet der Tester das Programm als Blackbox, d.h. der Tester ist nicht an
dem internen Verhalten und an der Struktur des Programms interessiert, sondern
daran, Umstände zu entdecken, bei denen sich das Programm nicht gemäß den
Anforderungen verhält.
Die Testdaten werden nur aus der Spezifikation (Aus-/Eingabeparameter sowie die
Funktion des Programms) abgeleitet, d.h. ohne spezielle Kenntnis der internen
Struktur.
Äquivalenzklassen Wie oben hergeleitet soll hier die Untermenge von Testfällen bestimmt werden, die
mit höchster Wahrscheinlichkeit Fehler enthält. Zur Bestimmung dieser Untermenge
sollte man wissen, dass ein gut ausgewählter Testfall zwei weitere Eigenschaften
haben sollte:
Ronny Boost - 7 - 21.12.2005
Michael Künne
• er reduziert die Anzahl anderer Testfälle, die man entwickeln müsste um ein
vorgegebenes Ziel beim Testen zu erreichen
• er überdeckt eine große Menge anderer möglicher Testfälle, d.h. er sagt
etwas über die An- oder Abwesenheit von Fehlern für diesen speziellen Satz
von Eingabedaten aus
Letzteres bedeutet, wenn ein Testfall in einer Äquivalenzklasse einen Fehler
entdeckt, so erwartet man, dass alle anderen Testfälle in einer Äquivalenzklasse den
gleichen Fehler finden. Anders herum ausgedrückt, wenn ein Testfall den Fehler
nicht entdeckte, so erwarten wir, dass kein anderer Testfall in der Äquivalenzklasse
diesen Fehler findet.
Diese beiden Überlegungen führen zu einer Blackbox-Strategie, die als
Äquivalenzklassenbildung bekannt ist. Die zweite Bedingung dient zur Entwicklung
einer Menge von Bedingungen, die getestet werden sollen. Mit der ersten
Überlegung wird dann ein Minimalsatz von Testfällen definiert, der diese
Bedingungen vollständig erfasst.
2.4 Whitebox-Test
Die interne Struktur des Programms ist beim Whitebox-Test zentraler Dreh und
Angelpunkt der Untersuchung. Bei dieser Strategie definiert der Tester die Testdaten
mit Kenntnis der Programmlogik.
Beim Whitebox-Testen betrachtet man den Anteil des Programms (source code), der
durch die Testfälle ausgeführt oder angesprochen wird. Hierzu dienen folgende
Strategien [7]:
• Line coverage (Ausführung aller Anweisungen) Hier soll jede Anweisung im Programm wenigstens einmal ausgeführt werden.
Das Verhältnis von ausgeführten zu vorhandenen Anweisungen wird als C0-
Überdeckung bezeichnet. Bei 100% C0-Überdeckung führen die Testfälle jede
Anweisung mindestens einmal aus.
• Decision oder branch coverage Dies ist eine strengere Strategie für die Erfassung der Logik, bei der alle
Entscheidungen oder Sprünge ausgeführt werden. Bei diesem Verfahren
Ronny Boost - 8 - 21.12.2005
Michael Künne
müssen hinreichend viele Testfälle entworfen werden, so dass bei jeder
Entscheidung sowohl der THEN-Zweig als auch der ELSE-Zweig mindestens
einmal durchlaufen wird. Das Verhältnis von ausgeführten zu vorhandenen
Entscheidungspfaden (z.B. IF/ELSE-Zweige) wird als C1-Überdeckung
bezeichnet. Bei 100% C1-Überdeckung liefert jede Entscheidung mindestens
einmal TRUE und einmal FALSE. Man kann zeigen, dass diese Strategie
auch die Forderung nach der Ausführung eines jeden Befehls erfüllt.
2.5 Zusammenfassung
All diese Testverfahren sind in der Theorie sehr schön, jedoch werden sie nur zu
einem geringen Teil in der Praxis so eingesetzt. Am praktikabelsten bei diesen
Methoden sind die Komponenten- und Integrationstests. Sie sind am einfachsten zu
realisieren, sie sind kombinierbar, reproduzierbar und es gibt viele Hilfsprogramme,
die einem die Arbeit erleichtern können. In Java heißt hier das Zauberwort „JUnit“.
Ronny Boost - 9 - 21.12.2005
Michael Künne
3 Testen mit JUnit
Nun ist blindes „Drauflostesten“ vielleicht nicht besonders klug. Darum stellt sich die
Frage nach sinnvollen Kriterien für Softwaretests. Gute Deklarationen liefert die
Literaturrecherche [8]:
• Strikte Trennung des Testcodes vom Programmcode gewährleistet schlanke
Klassen und kleinere Softwarepakete zur Auslieferung.
• Testfälle sind vollkommen unabhängig voneinander ausführbar. Es gibt keine
festgelegte Reihenfolge oder … Abhängigkeiten der Testfälle untereinander.
• Das Ergebnis der Tests ist auf einen Blick eindeutig erkennbar, dadurch ist
keine Auswertung von Logfiles oder kryptischer Konsolenausgaben
notwendig.
3.1 JUnit
JUnit ist ein frei verfügbares, unter der IBM Public License stehendes Framework für
die Durchführung von Black-Box-Tests. Testfälle werden in eigenen Testklassen
kodiert und zu Testsuiten zusammengefasst. Somit ist die strikte Trennung des
Codes von Anwendung und Test sichergestellt. Diese Tatsache beinhaltet folgende
Vorteile:
• Bei Auslieferung der Software werden die Tests nicht mit ausgeliefert und
somit ist das Programm schlanker.
• Weitere, nicht mit der Anwendung vertraute Entwickler, können sich schneller
einarbeiten.
• Der Code der Anwendung bleibt selbst bei steigender Komplexität
übersichtlich.
Die grundlegende Einheit beim Testen mit JUnit ist der Testfall - in der Klasse
„TestCase“ gekapselt. Ergebnisse werden in „TestResult“ gespeichert. Die zentrale
Methode der Klasse „TestCase“ ist „TestCase.runTest(TestResult)“, die im Interface
„Test“ definiert wird. Zum Aufbau dieser Methode später mehr. Mehrere Testfälle
werden zu Gruppen in der Klasse „TestSuite“ zusammengefasst. Das Besondere an
„TestSuite“ ist, dass sie ebenfalls das Interface „Test“ implementiert. Wird dann
„TestSuite.runTest(TestResult)“ aufgerufen, gibt die Klasse den Aufruf an alle
enthaltenen Testfälle weiter. Das funktioniert auch, falls eine Testsuite nicht nur
echte Testcases enthält, sondern wieder Testsuiten. So ist das hierarchische
Schachteln von Testfällen möglich.
In Abbildung 3.1 ist das Klassenschema zu sehen. Das Design-Pattern, welches das
„Test“-Interface und die beiden Klassen „TestCase“ und „TestSuite“ verbindet, heißt
Composite. Wie in der Abbildung zu sehen, ist das Framework mit drei zentralen
Klassen und einem Interface sehr übersichtlich, was die Einarbeitung verkürzt und
den Einsatz vereinfacht. Die gesamte Distribution hat zwar momentan elf Klassen
und drei Interfaces, aber von den zusätzlichen Klassen ist nur die „Assert“-Klasse
von ähnlich zentraler Bedeutung (und auch sie nur indirekt).
Abbildung 3.1: Aufbau der wichtigsten Klassen des JUnit-Frameworks [2]
Die Klasse Assert mit den wichtigsten Asserts: class Assert (parent class of TestCase) - static assertTrue() basic assert function (used to be assert())
Ronny Boost - 10 - 21.12.2005
Michael Künne
Ronny Boost - 11 - 21.12.2005
Michael Künne
- static assertEquals() for all primitive types and Object - static assertNull() for Object - static assertNotNull() for Object - static assertSame() for Object - static assertNotSame() for Object - static assertFalse() for boolean condition - static fail() make a test fail
Zurück zum Aufbau der Runtest-Methode. Sie ruft intern die folgenden drei
Methoden auf:
„setUp()“
„runTest()“
„tearDown()“
Alle drei Methoden sind leer implementiert und können vom Entwickler
überschrieben werden. „setUp()“ und „tearDown()“ erzeugen eine geeignete
Testumgebung beziehungsweise geben sie frei. Die Methode „runTest()“ dagegen
enthält die eigentlichen Tests.
Durch Reflection - eine Java-Technik, die es ermöglicht, zur Laufzeit die Methoden
einer Klasse abzufragen - ist es möglich in der Testklasse nur eine Reihe von
Methoden nach dem Schema „void testXXX()“ anzulegen statt „runTest()“ zu
überschreiben. Durch den Konstruktor „TestSuite(MyTestClass.class)“ werden dann
automatisch alle Testmethoden innerhalb der Testsuite durchgeführt. Die
Reihenfolge der Ausführung der test – Methoden ist dabei nicht festgelegt und spielt
insofern keine Rolle, da die Methoden der Anwendung unabhängig voneinander das
gleiche Ergebnis liefern sollen.
Ronny Boost - 12 - 21.12.2005
Michael Künne
3.2 Installation
Die Installation ist einfach: das heruntergeladene Zip-File entpacken (Download von
www.junit.org) und das dann verfügbare Jar-Archiv zum „CLASSPATH“ des
jeweiligen Systems hinzufügen bzw. in die IDE einbinden.
3.3 Beispiel für einen Test
Im folgenden Listing ist ein Beispiel zum Anlegen von „Person“ – Objekten zu sehen: Listing 1: Person.java 01: class Person { 02: private String iVname, iNname; 03: 04: public Person(String v, String n) { 05: iVname = v; 06: iNname = n; 07: } 08: 09: public boolean equals(Object o) { 10: Person p = (Person) o; 11: return p.getVname() == iVname && p.getNname() == iNname; 12: } 13: 14: public String getVname() { 15: return iVname; 16: } 17: 18: 19: public String getNname() { 20: return iNname; 21: } 22: 23: public void setVname(String v) { 24: iNname = v; 25: } 26: 27: 28: public void setNname(String n) { 29: iNname = n; 30: } 31: 32: public String toString() { 33: return iVname + " " + iNname; 34: } 35: }
Die Objekte der Klasse Person.java enthalten die
- Attribute:
Ronny Boost - 13 - 21.12.2005
Michael Künne
o iVname = Vorname.
o iNname = Nachname.
- Methoden:
o Getter und Setter für die Attribute.
o toString(), um aus Vor- und Nachname den vollständigen Namen
zusammenzusetzen.
und den Konstruktor, welchem alle o.g. Attribute übergeben werden.
Zum Testen der Methoden des Anwendungscodes dient die Klasse PersonTest.java. Listing 2: PersonTest.java 01: import junit.framework.*; 02: 03: public class PersonTest extends TestCase { 04: 05: private Person iAuthor,iAuthor2, iAuthor3, iTux, iTux2; 06: private String iName; 07: 08: public PersonTest(String name) { 09: super(name); 10: } 11: 12: public static void main(String[] args) { 13: junit.swingui.TestRunner.run(PersonTest.class); 14: } 15: 16: protected void setUp() { 17: iAuthor = new Person("Bernhard", "Bablok"); 18: iAuthor2 = new Person("Bernhard", "Bablok"); 19: iAuthor3 = new Person(null,null); 20: iAuthor3.setVname("Bernhard"); 21: iAuthor3.setNname("Bablok"); 22: iTux = new Person("Tux",null); 23: iTux2 = new Person("Tux",null); 24: iName = "Penguin"; 25: } 26: 27: public void testEquals() { 28: assertTrue(iAuthor.equals(iAuthor2)); 29: assertTrue(!iAuthor.equals(iName)); 30: assertTrue(iTux.equals(iTux2)); 31: } 32: 33: public void testSetName() { 34: assertEquals(iAuthor,iAuthor3); 35: } 36: 37: public void testGetNname() { 38: assertEquals(iAuthor.getNname(),"Bablok"); 39: } 40: 41: public void testToString() { 42: assertEquals(iAuthor.toString(), "Bernhard Bablok"); 43: }
44: }.
Das “…Test” im Namen ist nicht zwingend erforderlich, aber um die eindeutige
Zugehörigkeit zur zu testenden Klasse sicher zu stellen ist es sinnvoll hinter den
ursprünglichen Klassennamen der Anwendung das Suffix “…Test“ anzuhängen.
Im Gegensatz hierzu ist die Benennung der Prüfmethoden fest vorgegeben: Diese
müssen immer der Form „testXXX“() entsprechen, wobei für XXX ein beliebiger
Name stehen kann, wiederum vorzugsweise der Name der zu testenden Methode.
Der Ablauf des Test beginnt durch den Aufruf der main(…)-Methode und des darin
enthaltenen Methodenaufrufs von run(…) wie oben beschrieben. In der Methode
setUp() wird eine Testumgebung mit diversen Testobjekten angelegt. Auf diesen
Testobjekten werden nun die Testmethoden ausgeführt. Sobald ein assert(…)
fehlschlägt unterbricht JUnit die Ausführung und signalisiert dies mit einem roten
Balken und der Angabe bei welchem Test dies passierte. Hier beispielsweise schlägt
der Test fehl. Der hier verwendete TestRunner zeigt die in Abbildung 3.2 dargestellte
Ausgabe:
Abbildung 3.2 JUnit in Aktion: Zwei Fehler wurden gefunden, in der unteren Hälfte der Stack Trace dazu. [2]
Ronny Boost - 14 - 21.12.2005
Michael Künne
Nach Bereinigung aller Fehler sieht das Ergebnis wie in Abbildung 3.3 aus:
Abbildung 3.3: Ein erfolgreicher JUnit-Lauf. [2]
Es dient also ein einfacher grüner oder roter Balken als Bestätigung für den Erfolg
bzw. Misserfolg des Testvorgangs.
Um nun diese Tests in einer TestSuite zu integrieren ist nicht mehr viel Aufwand
vonnöten: Listing 3: AllTests.java 01: import junit.framework.*; 02: 03: public class AllTests { 04: 05: public static Test suite() { 06: TestSuite suite = new TestSuite("Test for default package"); 07: suite.addTestSuite(PersonTest.class); 08: return suite; 09: } 10: }
3.4 Test Driven Development
In obigem Beispiel wurde der Test erst nach Erstellen der Klasse „Person.java“ für
die darin enthaltenen Programmteile implementiert. Eine andere Vorgehensweise
wäre die Überlegung der Test-First-Strategie. Dabei gilt es einen Test für eine Ronny Boost - 15 - 21.12.2005
Michael Künne
Ronny Boost - 16 - 21.12.2005
Michael Künne
Softwarekomponente - bei der objektorientierten Programmierung in der Regel
Klassen - vor der eigentlichen Komponente zu implementieren.
Der Entwickler einer Klasse ist sich in der Regel klar darüber, was er mit seiner
Klasse erreichen möchte. So entsteht eine Art Protokoll für die Anwendung der
Klasse. Und genau dieses Protokoll wird von einem Unit Test geprüft. Die zu
entwickelnde und zu testende Klasse wird dabei immer nur so weit ausprogrammiert,
wie es notwendig ist, um einen Test zu erfüllen. Nach jedem erfolgreichen Test wird
dann der entwickelte Code in kurzen Zyklen weiter verfeinert und erneut getestet, bis
dieser die Geschäftslogik erfüllt und in einem optimalen Zustand ist. Neudeutsch
spricht man hier von Refactoring. Der Vorteil dieses Verfahrens ist es, stets über
einen funktionierenden Code zu verfügen, der leicht automatisiert getestet werden
kann.
In der testgetriebenen Softwareentwicklung geht der Entwickler folgendermaßen vor:
- Der erste Schritt ist das Schreiben eines Tests für die zu erfüllende
Teilfunktion
- Der Code der Anwendung wird nun so geschrieben, um dem Test zu genügen
oder anders ausgedrückt: damit der Test fehlerfrei durchläuft.
- Während der Implementierung wird nun abwechselnd am Programm- bzw.
Testcode geschrieben, wodurch kleine Iterationen entstehen, welche in sehr
kurzen Zeitabständen (5 – 20 Minuten) erfolgen.
- Bevor der neu entstandene Code in das Gesamtsystem integriert werden darf,
müssen alle Tests fehlerfrei laufen.
Die hierbei immer wieder auftretende Reihenfolge beim Testen ist grün – rot – grün –
rot …, d.h. nach Schreiben des Tests kann dieser selbstverständlich noch nicht erfüllt
werden, da noch kein Code zum Erfüllen der Testbedingung vorhanden ist. Nun
schreibt der Entwickler so wenig Code wie möglich, um den Test zu erfüllen bis der
JUnit-Test als Ergebnis den oben erläuterten grünen Balken ausgibt. Dann beginnt
der Ablauf von vorne mit dem Schreiben eines Tests. Der bis dahin geschriebene
Programmcode wird durch diese kleinen Iterationen mit verfeinert und bei jedem
Ausführen der Tests wieder geprüft. Dadurch wird sichergestellt, bereits positiv
getesteten Code nicht wieder zu verschlechtern.
Ronny Boost - 17 - 21.12.2005
Michael Künne
3.4.1 Vorteile des TDD
• Das TDD zwingt den Entwickler dazu, sich während der Entwicklung einer
Klasse und speziell noch vor der Implementierung Gedanken über die
Nutzung der Klasse zu machen.
• Fehler in der Implementierung werden frühzeitig vermieden oder erkannt, was
bei der abschließenden Fehlersuche enorm Zeit und damit Geld einspart. Ist
die Software allerdings im fortgeschrittenen Zustand, ist der Fehler nur unter
großem Aufwand (also teuer) zu beheben.
• Code soll funktional bleiben, also nicht regressiven Einflüssen während der
weiteren Entwicklung unterliegen.
• Kein unnötiger Code im Programm.
3.4.2 Nachteile des TDD
• Es sind gute bis sehr gute Kenntnisse im Entwerfen und Implementieren von
Software zwingend erforderlich.
• „Ohne Disziplin kein Erfolg“. Weicht der Entwickler von der Vorgehensweise
der kleinen Iterationen ab – schreibt beispielsweise der Entwickler in einer
kreativen Phase drei Klassen ohne vorher Tests zu schreiben – ist die
Methodik sehr schwer wieder aufzunehmen.
3.5 Konventionelle Softwareerstellung vs. TDD
Eine Gegenüberstellung von konventioneller Entwicklung in Abbildung 3.4 zu
testgetriebener Entwicklung in Abbildung 3.5 soll die Vorteile des Testcase Driven
Development (TDD) bezüglich Aufwand, Kosten und umgesetzter Anforderungen
verdeutlichen:
Abbildung 3.4: Konventioneller Projektablauf [3]
Abbildung 3.5: Testgetriebener Projektablauf [3]
Ronny Boost - 18 - 21.12.2005
Michael Künne
Ronny Boost - 19 - 21.12.2005
Michael Künne
4 Zusammenfassung JUnit eignet sich gut für nachvollziehbare, reproduzierbare Tests von
abgeschlossenen Einheiten (Units). Dadurch ist Refactoring des bereits
implementierten Codes begünstigt. Die prinzipielle Einfachheit mit der Tests
umgesetzt werden können, kombiniert mit beliebiger Erweiterbarkeit entsprechend
dem Wachstum des Programms ist die große Stärke von JUnit.
Es ist allerdings nicht das ultimative Werkzeug zur Behebung aller Fehler in Software
und deren Entwicklung, sondern stellt ein wichtiges zusätzliches Mittel, insbesondere
für die testgetriebene Softwareentwicklung und teilweisen Validierung bereits
vorhandener Projekte dar.
Im Hinblick auf das TDD sollte sich jeder Programmierer ernsthafte Gedanken über
seine Vorgehensweise beim Programmieren anstellen. Der Weg vom
„Drauflosprogrammieren“ zum strukturierten Vorgehen mittels Entwurf der wirklich
benötigten Funktionen vor dem ersten Zeichen Code ist ein Steiniger, aber
Lohnenswerter.
Ronny Boost - 20 - 21.12.2005
Michael Künne
5 JUnit Erweiterungen Projekt Beschreibung
StrutsTestCase Testen des JSP-Frameworks Struts.
JUnitX und XPTest Erweiterung auf Privat- und Protected-Klassen,
Integration in Together.
JUnit und TIBCO Integration in TIBCO Active Enterprise Application.
JUnit Test Generator Generierung von Testfällen.
Daedalus JUnit Extensions Ressourcen-Management für JUnit-Tests.
JFCUnit Unit-Tests für Swing-Applikationen.
JUnitPP Unter anderem Testdaten Repository,
Kommandozeilen-Argumente.
JSP TEST Testen von Java Server Pages.
JXUnit Trennt Testdaten und Testcode. Verwendet XML.
JUnitHelp Hilfe zu JUnit.
Joshua Regression-Testing mit JUnit.
JDepend Testen von Design-Metriken (misst die Komplexität der
Klassen).
JesTer Sucht Code, der nicht durch Tests abgedeckt wird.
HttpUnit Funktionstest von interaktiven, dynamischen
Webseiten.
JUnitEE Tests für J2EE-Anwendungen.
Cactus Testet Server-seitigen Java-Code.
UnitPerf Erweitert JUnit-Testklassen für Performance- und
Skalierungstests. Tabelle 5-1: JUnit Erweiterungen.
Ronny Boost - 21 - 21.12.2005
Michael Künne
6 Abbildungsverzeichnis Tabelle 2-1: Zusammenfassung verschiedener Testmethoden. 5
Abbildung 3.1: Aufbau der wichtigsten Klassen des JUnit-Frameworks [2] 10
Abbildung 3.2 JUnit in Aktion: Zwei Fehler wurden gefunden, in der unteren Hälfte
der Stack Trace dazu. [2] 14
Abbildung 3.3: Ein erfolgreicher JUnit-Lauf. [2] 15
Abbildung 3.4: Konventioneller Projektablauf [3] 18
Abbildung 3.5: Testgetriebener Projektablauf [3] 18
Tabelle 5-1: JUnit Erweiterungen. 20
Ronny Boost - 22 - 21.12.2005
Michael Künne
7 Quellen
[1] Frank Westphal „Testgetriebene Entwicklung“ 2005
[2] Bernhard Bablok „Reihenuntersuchung“;
Linux-Magazin Ausgabe 03/2002
[3] www.linuxenterprise.de 2004
[4] de.wikipedia.org
[5] „Das heilige Orakel“ www.google.de
[6] Robert Pintarelli Modul-Tests mit JUnit 2004
[7] Christian Aich, Robert Reeb Einführung in den Softwaretest 2004
[8] Barbara Beenen Rot-Grünes Wechselspiel 2004
top related