JUnit

Einstieg
Download und Installation
JUnit Testgrundlagen
Ein Beispiel
Initialisieren und Aufrämen
Junit und Ant
Links

Einstieg

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.

Download und Installation

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.

JUnit Test Grundlagen

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).

Ein Beispiel

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;
  }
}
itmapa.de - X2H V 0.18

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));
  }
}
itmapa.de - X2H V 0.18

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;
  }
}
itmapa.de - X2H V 0.18

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)
            

Initialisieren und Aufrämen

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.

JUnit und Ant

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/ Flagge Großbritanien

JUnit API Dokumentation
http://junit.sourceforge.net/javadoc/ Flagge Großbritanien

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/ Flagge Großbritanien

Erik Hatcher and Steve Lourghran
Java Development with Ant - Testing with JUnit Flagge Großbritanien
http://java.sun.com/developer/Books/javaprogramming/ant/ant_chap04.pdf Flagge Großbritanien

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 Flagge Großbritanien
JUnit 4.x Quick Tutorial
http://code.google.com/p/t2framework/wiki/JUnitQuickTutorial Flagge Großbritanien

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/