# JUnit: Testproblem.



## 7crystal7 (16. Jan 2008)

Hallo Leute,

ich lerne gerade nach http://www.frankwestphal.de/UnitTestingmitJUnit.html Unit Testing. Hierzu habe ich folgende Klasse Euro:


```
public class Euro {
	
  private long cents;
  
  public Euro(double euro) {
	 if (euro < 0.0) {
	    throw new IllegalArgumentException("Negative amount");
	 }
	 cents = Math.round(euro * 100.0);
  }
  
  public double getAmount() {
    return (cents / 100);
  }
  
  public int hashCode() {
	return (int) cents;
  }
  
  public Euro add(Euro other) {
	return new Euro(this.cents + other.cents);
  }
  
  public boolean equals(Object o) {
    if (o == null || !o.getClass().equals(this.getClass())) {
    	return false;
	}
	Euro other = (Euro) o;
	return this.cents == other.cents;
  }

}
```

und meine Testklasse:


```
import junit.framework.*;

public class EuroTest extends TestCase {

	  private Euro two;
	  
	  public EuroTest(String name) {
	    super(name);
	  }

	  protected void setUp() {
		two = new Euro(2.00);
	  }

	  protected void tearDown() {
		//two = null;
	  }
	  
	  public void testAmount() {
		assertTrue(2.00 == two.getAmount());
	  }

	  public void testRounding() {
		Euro roundedTwo = new Euro(1.995);
		assertEquals("rounded amount", 2.00, roundedTwo.getAmount(), 0.001);
	  }

	  public void testAdding() {
		Euro three = two.add(new Euro(1.00));
		assertEquals("two", 2.00, two.getAmount(), 0.001);
		assertEquals("sum", 3.00, three.getAmount(), 0.001);
	  }
	  
	  public void testEquality() {
		 assertEquals(two, two);
		 assertEquals(two, new Euro(2.00));
		 assertEquals(new Euro(2.00), two);

		 assertTrue(!two.equals(new Euro(0)));
		 assertTrue(!two.equals(null));
		 assertTrue(!two.equals(new Object()));
	  }
	  
	  public void testHashCode() {
		assertTrue(two.hashCode() == two.hashCode());
		assertTrue(two.hashCode() == new Euro(2.00).hashCode());
	  }
	  
	  public void testNegativeAmount() {
	    try {
	      final double NEGATIVE_AMOUNT = -2.00;
	      new Euro(NEGATIVE_AMOUNT);
	      fail("Should have raised an IllegalArgumentException");
		} 
		catch (IllegalArgumentException expected) {
			System.out.println("Exception works!");
		}
	  }
  
  public static void main(String[] args) {
    junit.swingui.TestRunner.run(EuroTest.class);
  }
}
```


Wieso funktioniert der testAdding nicht? Der Fehler lautet immer: 
junit.framework.AssertionFailedError: sum expected:<3.0> but was:<300.0>


Könnt ihr mir weiterhelfen? Wäre dringend weil ich das wirklich nicht verstehe...

Vielen Dank,
Babsi


----------



## quippy (16. Jan 2008)

Your code works as designed! Und Junits funktionieren doch 

Schau mal in Deinen Konstruktor der Klasse €uro und dann dort in die Add-Methode. Wenn Du die Cents addierst (zwei Long-Werte) und keinen Konstruktor für long als Extrabehandlung hast, dann werden die Cents (200+100) nochmal mit hundert multipliziert und bei getAmount wieder durch 100 geteilt.

Deine add-Methode müßte lauten

```
return new Euro((this.cents + other.cents)/100.0); // Auf Float/Double achten, sonst wird das hier ganzzahlig
```

Wenn Du übrigens mal einen Debugger verwendet hättest, dann wäre Dir das auch so mal aufgefallen!


----------



## maki (16. Jan 2008)

> Wenn Du übrigens mal einen Debugger verwendet hättest, dann wäre Dir das auch so mal aufgefallen!


Logging ist besser


----------



## quippy (16. Jan 2008)

maki hat gesagt.:
			
		

> > Wenn Du übrigens mal einen Debugger verwendet hättest, dann wäre Dir das auch so mal aufgefallen!
> 
> 
> Logging ist besser



Logging ist das Debugging für Arme :bae: Also ne, da schaue ich lieber mal während der Laufzeit in den Code, als ihn mit Ausgaben zu spicken...


----------



## Guest (16. Jan 2008)

danke für die Antworten.. wie müsste ich denn die Klasse Euro modifizieren das der Test so wie er ist funktioniert?

Vielen Dank,
Babsi

P.S.: Wo stellt man das Logging und Debugging in Eclipse ein? Sorry -> Noob :-/


----------



## maki (16. Jan 2008)

> Logging ist das Debugging für Arme bae.gif Also ne, da schaue ich lieber mal während der Laufzeit in den Code, als ihn mit Ausgaben zu spicken...


Umgekehrt wird ein Schuh draus 

Logging ist Fehlersuche für Fortgeschrittene, Debugging ist für verzweifelte bzw. wenn man zwar weiss wo der Fehler ist, aber noch nicht warum es falsch läuft.

Mit einem guten Logging Konzept spart man sich in 90% den Debugger und die Zeit.


----------



## 7crystal7 (16. Jan 2008)

danke, schon gesehen sorry!

Wie kann ich denn in Eclipse einen Testcase debuggen?

LG
Babsi


----------



## 7crystal7 (16. Jan 2008)

ok, Debugging auch gefunden.. Kann man in Java nur nach unten steppen, nicht mehr zurück (also eine Zeile darüber z.B.)? Kenne das vom VB bidirektional..

Vielen Dank,
Babsi


----------



## Guest (16. Jan 2008)

7crystal7 hat gesagt.:
			
		

> Kann man in Java nur nach unten steppen


Ja.


----------



## tfa (16. Jan 2008)

7crystal7 hat gesagt.:
			
		

> Kann man in Java nur nach unten steppen



Du kannst in der Aufruf-Stack-Liste eine Methode auswählen und dann "Drop to Frame" aufrufen.


----------



## quippy (17. Jan 2008)

maki hat gesagt.:
			
		

> > Logging ist das Debugging für Arme bae.gif Also ne, da schaue ich lieber mal während der Laufzeit in den Code, als ihn mit Ausgaben zu spicken...
> 
> 
> Umgekehrt wird ein Schuh draus
> ...



Das sehe ich anders. 

Klar, wenn ich Logging betreibe, muß ich als Entwickler selbst entscheiden, wo die Knackpunkte im Programm sind, an denen ich irgendwelche Ausgaben sehen will - und zudem hinterher noch in der Lage sein, diese Ausgaben so zu interpretieren, daß reine Codeinspektion ausreicht, um den Fehler zu finden.

Aber wenn ich dann sowas sehe:


```
private void myMethod(int a, float b)
{
   System.out.println("Bin in myMethod");
   int x = irgendwas();
   System.out.println("X ist nun " + x);
   x += a;
   System.out.println("X ist nun " + x);
   x*=b;
   System.out.println("X ist nun " + x);
}
```

Dann bezweifle ich durchaus, ob Logging so eingesetzt wirklich gut ist. Da kann man auch sehr gut einmal mit dem Debugger durch, wenn's sein muss.


----------



## quippy (17. Jan 2008)

Anonymous hat gesagt.:
			
		

> danke für die Antworten.. wie müsste ich denn die Klasse Euro modifizieren das der Test so wie er ist funktioniert?
> 
> Vielen Dank,
> Babsi
> ...



Lesen, Liebes, lesen!



			
				quippy hat gesagt.:
			
		

> Schau mal in Deinen Konstruktor der Klasse €uro und dann dort in die Add-Methode. Wenn Du die Cents addierst (zwei Long-Werte) und keinen Konstruktor für long als Extrabehandlung hast, dann werden die Cents (200+100) nochmal mit hundert multipliziert und bei getAmount wieder durch 100 geteilt.
> 
> *Deine add-Methode müßte lauten*
> 
> ...



Oder hilft das nicht?


----------



## byte (17. Jan 2008)

maki hat gesagt.:
			
		

> > Logging ist das Debugging für Arme bae.gif Also ne, da schaue ich lieber mal während der Laufzeit in den Code, als ihn mit Ausgaben zu spicken...
> 
> 
> Umgekehrt wird ein Schuh draus
> ...


Sry aber das ist BS. :roll: Wie willst du per Logging den aktuellen Zustand der Objekte zur Laufzeit inspizieren? Du kannst wohl schlecht alle Werte mitloggen, ohne die Performance in den Keller zu treiben.


----------



## maki (17. Jan 2008)

> Sry aber das ist BS. icon_rolleyes.gif Wie willst du per Logging den aktuellen Zustand der Objekte zur Laufzeit inspizieren? Du kannst wohl schlecht alle Werte mitloggen, ohne die Performance in den Keller zu treiben.


@byto
Kein BS, sondern Praxis.

Die Testmaschine loggt prinzipiell mit Debug Level für die interessanten Klassen, da sind die Tests um einiges aussagekräftiger und Fehler viel einfacher reproduzierbar.

Performance im Keller?
Sorry, aber das nehme ich nicht ernst. 
Unsere Testmaschinen sind zwar schon 2 Jahre alt, aber ein Compaq/HP Sever mit 4 Xeon Prozesoren treibt das nicht in den Keller, dasselbe gilt für die Entwicklungsmaschinen, Duo Core.
Und auf meinem alten P4 war das auch nie ein Problem, den Debugger anzuschmeissen dauerte allerdings immer etwas 

Auf der Prod Maschine das Log level zur Laufzeit zu verändern (ohne neustart) ist auch nicht sonderlich schwer, falls gewünscht.

Abgesehen davon gibt es einfach Systeme die man nicht sinnvoll debuggen kann.



> private void myMethod(int a, float b)
> {
> System.out.println("Bin in myMethod");
> int x = irgendwas();
> ...


@quippy
Das ist kein Logging! Höchstens Logging für ganz ganz arme...
Log4j ist mein Favourit.

Man muss auch nicht groß entscheiden was wichtig ist.
Schreib einfach am Anfang jeder public Methode (einfache getter und settern ausgenommen) den Namen der Methode und die übergebenen Parameter rein, das reicht für 80-90% aller Fälle und du musst gar nicht erst den debugger anwerfen, bei den anderen füge ich paar mehr logging Ausgaben hinzu.

Schon ma 'ne email von System bekommen wenn auf der Prod Maschine was zusammenbricht? Mit log4j gar kein Problem.


----------



## tfa (17. Jan 2008)

maki hat gesagt.:
			
		

> > Sry aber das ist BS. icon_rolleyes.gif Wie willst du per Logging den aktuellen Zustand der Objekte zur Laufzeit inspizieren? Du kannst wohl schlecht alle Werte mitloggen, ohne die Performance in den Keller zu treiben.
> 
> 
> @byto
> Kein BS, sondern Praxis.



Klar ist Logging Praxis, ebenso wie Debugging - beides sehr wichtige Hilfsmittel bei der Softwareentwicklung. 
Ich würde nicht sagen, das eine sei besser als das andere. Beides hat eben verschiedene Anwendungsbereiche.
Ich glaube byto wollte sagen, dass es unsinnig ist, jede Programmzeile mitzuloggen. Oder würdest Du (um mal auf Babsis Problem vom Anfang zurückzukommen) folgendes machen?

```
public Euro(double euro) { 
    LOG.debug("Neues Euro-Objekt anlegen mit euro="+euro);
    if (euro < 0.0) { 
       throw new IllegalArgumentException("Negative amount"); 
    } 
    cents = Math.round(euro * 100.0); 
    LOG.debug("Eurobetrag umgerechnet in Cents="+cents);
  }
```

Sicherlich nicht. Hätte man aber, um dieses Fehler zu finden.
Wie groß und unübersichtlich wären dann die Log-Dateien und wie lange bräuchte man, um was vernünftiges darin zu finden?

Ich meine, die richtige Antwort auf einen fehlgeschlagenen Unit-Test ist Debugging (und nur Debugging). 
Man weiß ja sogar schon, wo man den Breakpunkt machen muss. Warum also Log-Datein durchsuchen?


----------



## byte (17. Jan 2008)

Eben. Es ist einfach viel komfortabler, wenn ich einfach per Breakpoint in die Methode springe, wo etwas faul ist und ich auf einen Blick die Zustände alle relevanten Objekte sehen kann. Und eine App im Debugmodus zu starten, dauert kaum länger als normal. Im übrigen kannst Du Hot Code Replacement beim Debuggen benutzen, musst also gar nicht ständig die Anwendung neu starten, wenn Du Code änderst.
Mag aber sein, dass es mehrschichtige EE Anwendungen gibt, wo Debugging nicht so toll geht. Das kann ich nicht so beurteilen, weil ich bisher wenig mit App.servern gearbeitet habe.


----------



## quippy (17. Jan 2008)

> Man muss auch nicht groß entscheiden was wichtig ist.
> Schreib einfach am Anfang jeder public Methode (einfache getter und settern ausgenommen) den Namen der Methode und die übergebenen Parameter rein, das reicht für 80-90% aller Fälle und du musst gar nicht erst den debugger anwerfen, bei den anderen füge ich paar mehr logging Ausgaben hinzu.
> 
> Schon ma 'ne email von System bekommen wenn auf der Prod Maschine was zusammenbricht? Mit log4j gar kein Problem.



Wir nutzen hier auch Log4J und das ist schon super. Allerdings würde die Taktik von Dir die Logs der Server schon ziemlich vollpumpen. Klar, man kann je nach Kontext/Pakage einstellen, wie der Loglevel sein soll, aber performant ist das trotzdem nicht unbedingt! 

Vor allem, wenn man das so macht:

```
private void function(int a, ComplextType[] array)
{
   if (Log.isDebug(CATEGORY))
       Log.debug(CATEGORY, "function:  " + a + ", " + Array.toString(array));
}
```

Diese ganzen Stringkonkatenierungen sind nur für's Debugging da und das macht alles wirklich langsam. Der vorherige Test mit "if Log.isDebug()" hilf da schon, trotzdem - das würde ich lassen.

Ich persönlich streue Loggings nur dort ein, wo ich bei einem Produktionsfehler schnell im Log sehen können will, was da wohl schief gelaufen sein könnte - wärend der Entwicklung benutze ich grundsätzlich Debugging. Vom Logging bleibt nur das im Coding, was wie gesagt in der Produktivumgebung benötigt wird - und das sollte einfach nicht zu viel sein! Wir haben da jedenfalls schon mal viel Energie reingesteckt, um Performanceeinbußen durch übertriebenes Logging (gerade wegen der Stringverarbeitung) auszubauen!


----------



## quippy (17. Jan 2008)

byto hat gesagt.:
			
		

> Mag aber sein, dass es mehrschichtige EE Anwendungen gibt, wo Debugging nicht so toll geht. Das kann ich nicht so beurteilen, weil ich bisher wenig mit App.servern gearbeitet habe.



Du kannst (z.B. bei Eclipse) dich an einen JBoss im Debug-Modus dranklemmen und wie gewohnt debuggen. Bei verteilten Systemen mit EJBs etc. ist das allerdings nicht immer ganz trivial!


----------



## maki (17. Jan 2008)

@tfa


> Klar ist Logging Praxis, ebenso wie Debugging - beides sehr wichtige Hilfsmittel bei der Softwareentwicklung.


Natürlich geht es nicht ohne Debugging, dass hab ich ja auch nicht behauptet, aber es gibt Situationen, in denen Debugging nicht möglich/sinnvoll ist.
Abgesehen von EJBs ist man in komplexeren Frameworks mit Debugging auch nicht gut beraten, die Komplexität erschlägt einen.



> Ich glaube byto wollte sagen, dass es unsinnig ist, jede Programmzeile mitzuloggen.


Da stimme ich zu.



> Oder würdest Du (um mal auf Babsis Problem vom Anfang zurückzukommen) folgendes machen?


Mag sein das man mich jetzt auslacht, aber manchmal mach ich das, weil ich muss/es besser ist.
Ein Logging statement schreib ich nur ein einziges mal, kann es immer auswerten.
Zugegeben, in diesem Beispiel wäre Debuggen möglich und besser weil effizienter und weniger aufwendig.



> Ich meine, die richtige Antwort auf einen fehlgeschlagenen Unit-Test ist Debugging (und nur Debugging).


Das sehe ich anders, "unser" DAO Framework zB lässt sich kaum sinnvoll Debuggen, meine Unittests für die DAOs können nur selten sinnvoll debuggt werden.
Da kommt es aufs log an und was in der DB angekommen ist.
Steht ja genau da was wo passiert ist bevor die Exception geworfen wurde.



> Man weiß ja sogar schon, wo man den Breakpunkt machen muss. Warum also Log-Datein durchsuchen?


Wenn ich weiss wo der Fehler war und welche Werte diesen Fehler verursacht haben, hab ich es meist nicht mehr nötig den Debugger anlaufen zu lassen.
Ich suche übrigens nach dem Methodennamen im Log um die Werte herauszufinden  
Wenn die Log Einstellungen richtig sind, hat man auch nicht zuviel "Schrott" über den man lesen muss.

@byto


> Es ist einfach viel komfortabler, wenn ich einfach per Breakpoint in die Methode springe, wo etwas faul ist und ich auf einen Blick die Zustände alle relevanten Objekte sehen kann. Und eine App im Debugmodus zu starten, dauert kaum länger als normal. Im übrigen kannst Du Hot Code Replacement beim Debuggen benutzen, musst also gar nicht ständig die Anwendung neu starten, wenn Du Code änderst.


Tomcat im Debug Modus starten dauert ein bisschen, geht aber meist problemlos, allerdings funzt der Hot Code Replacement nicht, damit wäre nach jeder Änderung ein Neustart erfoderlich(ausser bei Änderungen in JSPs).
Das wars dann auch schon mit der Komfortablität...

Naja, was für mich funktioniert muss ja nicht für jeden funktionieren, kann dir aber sagen, das der Debugger bei mir eher die Ausnahme bei der Fehlersuche ist.


----------



## maki (17. Jan 2008)

> Diese ganzen Stringkonkatenierungen sind nur für's Debugging da und das macht alles wirklich langsam. Der vorherige Test mit "if Log.isDebug()" hilf da schon, trotzdem - das würde ich lassen.


Wenn das if drinn ist, das ist es eine einzige Abfrage die gemacht wird, da geht nix an Performance verloren was du merken könntest.

Abgesehen davon sind diese String Verkettungen nicht wirklich so langsam, da intern StringBuilder/StringBuffer verwendet wird, einfach mal den Kompilierten Code dekompilieren.
Was Zeit kostet ist die Daten zu Loggen, nicht die Stringverkettung, wenn nicht geloggt wird (die if Abfrage), kostet es auch keine echte Performance.


----------



## quippy (17. Jan 2008)

maki hat gesagt.:
			
		

> > Diese ganzen Stringkonkatenierungen sind nur für's Debugging da und das macht alles wirklich langsam. Der vorherige Test mit "if Log.isDebug()" hilf da schon, trotzdem - das würde ich lassen.
> 
> 
> Wenn das if drinn ist, das ist es eine einzige Abfrage die gemacht wird, da geht nix an Performance verloren was du merken könntest.



In meinem Post steht ja drin, daß durch die Abfrage das schon besser ist - aber wenn das Logging für diesesen Kontext auf Debug-Level steht, dann greift's auch.
Ich weiß nun nicht, was Java5 aus den Stringkonkatenierungen so macht, aber J4 und kleiner hat da doch sowas gemacht:

```
aus
   String a = "hallo" + " - " + " Welt";
wird
   StringBuffer t1 = new StringBuffer("hallo");
   t1.append(" - ");
   String t2 = t1.toString();
   StringBuffer t3 = new StringBuffer(t2);
   t3.append(" Welt");
   String a = t3.toString();
```
Oder war das auch schon besser? Viele Entwickler (mich eingeschlossen) formulieren zeitkritische Stringverarbeitungen ja lieber manuell mit StringBuffer - was ja durchaus historisch bedingt sein kann.

Übrigens frage ich mich nun wirklich, ob wir die Diskussion nicht beenden oder in einen anderen Thread verscheiben sollten. Wir sind alle eigentlich der gleichen Meinung - jeder verwendet Logging oder Debugging so, wie es für Ihn im konkreten Fall notwendig ist.


----------



## quippy (17. Jan 2008)

```
public class Euro {
	
  private long cents;
  
  public Euro(double euro) {
	 if (euro < 0.0) {
	    throw new IllegalArgumentException("Negative amount");
	 }
	 cents = Math.round(euro * 100.0); // <----- Du verwendest CENTS
  }
  
  public double getAmount() {
    return (cents / 100); // <---- und konvertierst hier!
  }
  
  public int hashCode() {
	return (int) cents;
  }
  
  public Euro add(Euro other) {
	//return new Euro(this.cents + other.cents); // <----- Hier ist der Fehler
	//return new Euro((this.cents + other.cents) / 100); // möglicher FIX
	return new Euro(this.getAmount() + other.getAmount()); // der bessere FIX
  }

  public boolean equals(Object o) {
    if (o == null || !o.getClass().equals(this.getClass())) {
    	return false;
	}
	Euro other = (Euro) o;
	return this.cents == other.cents; // <--- Das ist OK
  }

}
```

Hier nochmal der FIX für Babsi, weil ich nicht weiß, ob Sie's nun gelesen hat!


----------



## maki (17. Jan 2008)

> Oder war das auch schon besser? Viele Entwickler (mich eingeschlossen) formulieren zeitkritische Stringverarbeitungen ja lieber manuell mit StringBuffer - was ja durchaus historisch bedingt sein kann.


Eher historisch und heute gar nicht mehr so gut:
http://java.sun.com/developer/technicalArticles/Interviews/community/kabutz_qa.html



> Übrigens frage ich mich nun wirklich, ob wir die Diskussion nicht beenden oder in einen anderen Thread verscheiben sollten. Wir sind alle eigentlich der gleichen Meinung - jeder verwendet Logging oder Debugging so, wie es für Ihn im konkreten Fall notwendig ist.


Auch wieder richtig!


----------



## tfa (17. Jan 2008)

quippy hat gesagt.:
			
		

> Ich weiß nun nicht, was Java5 aus den Stringkonkatenierungen so macht, aber J4 und kleiner hat da doch sowas gemacht:
> 
> ```
> aus
> ...



Das glaub ich nicht. Die Konkatenation von Stringliteralen wurde schon immer (mindestens seit 1.3) korrekt aufgelöst (also in String a="hallo - Welt") -- jedenfalls von Suns javac (bei z.B. jikes sah das damals anders aus).
Dass für jeden String ein neuer Buffer angelegt wurde, wage ich auch zu bezweifeln.

Abgesehen von der Performance würde mich das Zumüllen des Quelltextes durch exzessives Logging glaub ich auch stören. Eine Zeile ist ok, aber dann noch ein if davor...


----------

