JUnit ist ein Framework zum Schreiben wiederholbarer, automatisierter Tests für Java-Programme. JUnit eignet sich im Besonderen für Unit-Tests einzelner Klassen oder Methoden. Diesem Artikel / Tutorial ist die Version 4.8 von JUnit zugrunde gelegt. Um JUnit 4.x verwenden zu können ist Java in der Version 5.0 (1.5) oder höher erforderlich. Der wichtigste Grund für die Notwendigkeit dieser Java-Version ist die Verwendung von Annotations zur Definition von Tests / Testabläufen. Ein weiterer Grund sind z.B. Generics, die in JUnit 4 Verwendung finden.
JUnit kann von der Seite http://sourceforge.net/projects/junit/files/junit/
heruntergeladen werden. Empfohlen wird, sich die entsprechende zip-Datei herunterzuladen, da
in diese auch Dokumentation zu JUnit enthält.
Da JUnit eine Bibliothek und keine Anwendung ist, lässt sich JUnit auch nicht installieren.
Um JUnit zu verwenden muss die Datei junit-4.8.jar
im Classpath des
verwendenden Projektes liegen. Diese jar-Datei befindet sich im Hauptverzeichnis
des entpackten zip-Files.
Assertions (Behauptungen) bilden die Grundlage für Tests mit JUnit.
Dazu werden in der Klasse org.junit.Assert
verschiedene
assertXXX(...)
und fail(...) Methoden definiert. Nachfolgend
einige Beispiele aus der Klasse Assert
:
assertTrue(boolean condition)
assertFalse(boolean condition)
assertEquals(Object expected, Object actual)
assertNotNull(Object object)
fail()
Die vollständige Liste der zur Verfügung stehenden Assertions kann der API-Dokumentation zu JUnit entnommen werden. Die API-Dokumentation ist auch Bestandteil des heruntergeladenen zip-Files (Unterverzeichnis JavaDoc).
In diesem Abschnitt soll die Arbeit mit JUnit an einem Beispiel vorgestellt werden.
Die nachfolgende Klasse MyClass
stellt die zu testende
Klassee dar. Diese Klasse enthält 2 Methoden, welche
offensichtlich fehlerhaft implementiert sind. Die erste Methode
divideByFour(int value)
soll einen übergebenen Wert
durch vier teilen und zurückgeben. Die Fehlerhafte Implementierung
gibt den übergebenen Wert durch 2 geteilt zurück.
Die zweite Methode opposite(boolean boolValue)
soll den übergebenen Parameter negiert zurückgebeb,
was die Implementierung aber offensichtlich nicht tut.
public class MyClass{ public int divideByFour(int value){ return value/2; } public boolean opposite(boolean boolValue){ return boolValue; } }
Die MyTest
ist die JUnit Testklasse. Mit Hilfe
dieser Klasse soll die korrektheit der Klasse MyClass
überprüft werden.
import org.junit.* ; import static org.junit.Assert.* ; public class MyTest{ @Test public void doTest1(){ System.out.println("Start doTest1"); MyClass myClass = new MyClass(); assertEquals(myClass.divideByFour(8),2); } @Test public void doTest2(){ System.out.println("Start doTest2"); MyClass myClass = new MyClass(); assertTrue(myClass.opposite(false)); } }
Die Methoden, die von JUnit zum testen aufgerufen werden sollen, werden
mit der Annotation @org.junit.Test
gekennzeichnet.
Da org.junit.*
in der Testklasse importiert wird,
braucht als Annotation auch nur @Test
an den entsprechenden
Methoden angegeben werden. Methoden, die nicht mit dieser Annotation
gekennzeichnet sind, werden von JUnit auch nicht zum testen aufgerufen.
Der statische Import von org.junit.Assert.*
führt dazu,
das die statischen Methoden der Klasse Assert
wie
assertEquals
und assertTrue
direkt aufgerufn werden
können.
Um JUnit die Tests durchführen lassen zu können müssen diese zunächst mit der Java Kompilierer mit folgendem Befehl in Java-Bytecode übersetzt werden:
javac *.java
Hier wird vorausgesetzt, dass sich das JUnit Bibliotheks Jar-File (z.B. junit-4.8.jar) in der Umgebungsvariable CLASSPATH gesetzt ist. Die Ausführung der Tests erfolgt dan mit dem Befehl:
java org.junit.runner.JUnitCore MyTest
Befindet sich JUnit nicht im aktuellen Umgebungsvariablen CLASSPATH
kann der Pfad zu JUnit mit Hilfe des Arguments "-cp
"
angegeben werden:
javac -cp .;VerzeichnisUndNameVonJUnit.jar *.java java -cp .;VerzeichnisUndNameVonJUnit.jar org.junit.runner.JUnitCore MyTest
Die Ausführung des Test auf der Konsole führt zur Ausgabe folgenden Testergebnisses, welches auf die 2 Fehler in der zu testenden Klasse hinweist:
JUnit version 4.8 .Start doTest1 E.Start doTest2 E Time: 0,015 There were 2 failures: 1) doTest1(MyTest) java.lang.AssertionError: expected:<4> but was:<2> at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.failNotEquals(Assert.java:645) at org.junit.Assert.assertEquals(Assert.java:126) at org.junit.Assert.assertEquals(Assert.java:470) at org.junit.Assert.assertEquals(Assert.java:454) at MyTest.doTest1(MyTest.java:10) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:24) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.junit.runner.JUnitCore.run(JUnitCore.java:117) at org.junit.runner.JUnitCore.runMain(JUnitCore.java:98) at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:53) at org.junit.runner.JUnitCore.main(JUnitCore.java:45) 2) doTest2(MyTest) java.lang.AssertionError: at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.assertTrue(Assert.java:43) at org.junit.Assert.assertTrue(Assert.java:54) at MyTest.doTest2(MyTest.java:17) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:24) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.junit.runner.JUnitCore.run(JUnitCore.java:117) at org.junit.runner.JUnitCore.runMain(JUnitCore.java:98) at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:53) at org.junit.runner.JUnitCore.main(JUnitCore.java:45) FAILURES!!! Tests run: 2, Failures: 2
Als nächstes folgt nun die Klasse MyClass.java
in der die "eingebauten" Fehler behoben sind.
public class MyClass{ public int divideByFour(int value){ return value/4; } public boolean opposite(boolean boolValue){ return !boolValue; } }
Eine erneute Durchführung des JUnit Tests zeigt, das die Tests nun ohne Erkennung eines Fehlers durchlaufen.
JUnit version 4.8 .Start doTest1 .Start doTest2 Time: 0 OK (2 tests)
Die Reihenfolge, in der die Test aus einer Klasse aufgerufen werden ist nicht festgelegt. So kann zum Beispiel ein Test der weiter unten im Quelltext einer Klasse definiert ist, vor dem Test welcher weiter oben definiert ist ausgeführt werden. Möchte man nun sicherstellen, das eine bestimmte Methode vor den Tests aufgerufen wird (um z.B. Datenbanken zu initialisieren) und eine bestimmte Methode nach den Tests aufgerufen wird kann muß man diese mit den Annotations
@BeforeClass
bzw.@AfterClass
kennzeichnen.
Methoden, die mit
@Before
bzw.@After
werden vor bzw. nach jeder Testmethode aufgerufen.
Dieser Abschnitt soll einen Einstieg in der Verwendung von JUnit mit
Ant aufzeigen. In diesem Abschnitt wird davon ausgegangen, dass das
JUnit Jar File in der Umgebungsvariable CLASSPATH
enthalten ist.
Im ersten Schritt soll das Ant-Skript erstellt werden, welches
eine möglichst einfache Verwendung des junit
Ant-Task zeigt.
Es wird davon ausgegangen, dass sich das Ant-Skript build.xml
und die zwei Klassen MyClass
und MyTest
sich im selben Verzeichnis befinden.
<project default="test"> <target name="test"> <echo>Ant und Junit Testbeispiel</echo> <junit> <test name="MyTest"/> </junit> </target> </project>
Wenn wir dieses Beispiel auf unseren aktuellen Test anwenden, der keinen fehlschlagenden Test enthält, sehen wir in der folgenden Ausgabe nicht, dass JUnit Tests ausgeführt wurden.
Buildfile: build.xml test: [echo] Ant und Junit Testbeispiel BUILD SUCCESSFUL Total time: 0 seconds
Um nun doch zu sehen, dass JUnit Tests durchgeführt wurden
verwenden wir nun mit dem Ant-Skript die ursprünglich, fehlerhafte
Klasse MyClass
. Der Ablauf des Ant-Skripts führt dann
zur folgenden Ausgabe:
Buildfile: build.xml test: [echo] Ant und Junit Testbeispiel [junit] Test MyTest FAILED BUILD SUCCESSFUL Total time: 0 seconds
Download JUnit
http://sourceforge.net/projects/junit/files/junit/
JUnit API Dokumentation
http://junit.sourceforge.net/javadoc/
Frank Westphal
Unit Tests mit JUnit
http://www.frankwestphal.de/UnitTestingmitJUnit.html
Frank Westphal
JUnit 4.0
http://www.frankwestphal.de/JUnit4.0.html
JUnit.org Resources for Test Driven Development
http://www.junit.org/
Erik Hatcher and Steve Lourghran
Java Development with Ant - Testing with JUnit
http://java.sun.com/developer/Books/javaprogramming/ant/ant_chap04.pdf
Martin Henze, Lukas Nießen
JUnit: Einführung und Anwendug
http://www-users.rwth-aachen.de/Martin.Henze/hn07.pdf
Technische Universität Darmstadt
JUnit 4 Tutorial
http://www.mm.informatik.tu-darmstadt.de/courses/helpdesk/junit4.html
t2framework
JUnit 4.x Quick Tutorial
http://code.google.com/p/t2framework/wiki/JUnitQuickTutorial
de.wikipedia.org/
JUnit
http://de.wikipedia.org/wiki/JUnit
Christian Ullenboom
JUnit 4 Tutorial, Java-Tests mit JUnit
http://www.tutego.de/blog/javainsel/2010/04/junit-4-tutorial-java-tests-mit-junit/