# JUnit



## schihab (23. Mrz 2009)

Hi

Habe folgenden code und soll den mit JUnit testen. Weiß aber nicht wirklich wie ich das machen soll. Könnt ihr mir bitte behilflich sein? Benutze Eclipse 3.4.


```
public class Muenzautomat extends Frame{
    // Anfang Attribute
    double muenz[][]= new double[2][8];
    int anzahl_muenzen[] =new int[8];
  private Panel panel1 = new Panel(null);
    private Button button1 = new Button();
    private TextField textField1 = new TextField();
    private TextArea textArea1 = new TextArea("", 1, 1, TextArea.SCROLLBARS_NONE);
    private Label label1 = new Label();
    private Label label2 = new Label();
    private Button button2 = new Button();
    private TextField textField2 = new TextField();
    private Label label3 = new Label();
  // Ende Attribute
  
  public void init()
  {
     muenz[0][0]=2;
     muenz[0][1]=1;
     muenz[0][2]=0.5;
     muenz[0][3]=0.2;
     muenz[0][4]=0.1;
     muenz[0][5]=0.05;
     muenz[0][6]=0.02;
     muenz[0][7]=0.01;
     for(int i=0;i<=7;i++)
     {
       muenz[1][i]=100;
     }
  }

  public Muenzautomat(String title) {
    // Frame-Initialisierung
    super(title);
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) { System.exit(0); }
    });
    int frameWidth = 585;
    int frameHeight = 347;
    setSize(frameWidth, frameHeight);
    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
    int x = (d.width - getSize().width) / 2;
    int y = (d.height - getSize().height) / 2;
    setLocation(x, y);
    Panel cp = new Panel(null);
    add(cp);
    init();
    // Anfang Komponenten


    panel1.setBounds(0, 0, 577, 313);
    cp.add(panel1);
    button1.setBounds(128, 104, 99, 25);
    button1.setLabel("Berechnen");
    button1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
        button1_ActionPerformed(evt);
      }
    });
    panel1.add(button1);
    textField1.setBounds(24, 104, 89, 24);
    textField1.setText("");
    panel1.add(textField1);
    textArea1.setBounds(280, 56, 289, 145);
    textArea1.setText("");
    panel1.add(textArea1);
    textArea1.setEditable(false);
    label1.setBounds(8, 160, 261, 16);
    label1.setText("");
    label1.setFont(new Font("MS Sans Serif", Font.PLAIN, 13));
    panel1.add(label1);
    label2.setBounds(152, 16, 213, 32);
    label2.setText("Münzautomat");
    label2.setFont(new Font("MS Sans Serif", Font.PLAIN, 30));
    panel1.add(label2);
    button2.setBounds(128, 264, 99, 25);
    button2.setLabel("Abfragen");
    button2.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
        button2_ActionPerformed(evt);
      }
    });
    panel1.add(button2);
    textField2.setBounds(24, 264, 89, 24);
    textField2.setText("");
    panel1.add(textField2);
    label3.setBounds(240, 264, 333, 24);
    label3.setText("");
    label3.setFont(new Font("MS Sans Serif", Font.PLAIN, 13));
    panel1.add(label3);
    // Ende Komponenten

    setResizable(false);
    setVisible(true);
  }

  // Anfang Methoden
    public void neuberechnen()
  {
    for(int i=0;i<=7;i++)
    {
      muenz[1][i]=muenz[1][i]-anzahl_muenzen[i];
      anzahl_muenzen[i]=0;
    }
  }
  
  public boolean pruefung(int i)
  {
    if((muenz[0][i]==2.0)&&(muenz[1][i]<=10))
    return true;
    if((muenz[0][i]==1.0)&&(muenz[1][i]<=10))
    return true;
    if((muenz[0][i]==0.5)&&(muenz[1][i]<=10))
    return true;
    if((muenz[0][i]==0.2)&&(muenz[1][i]<=10))
    return true;
    if((muenz[0][i]==0.1)&&(muenz[1][i]<=10))
    return true;
    if((muenz[0][i]==0.05)&&(muenz[1][i]<=10))
    return true;
    if((muenz[0][i]==0.02)&&(muenz[1][i]<=10))
    return true;
    if((muenz[0][i]==0.01)&&(muenz[1][i]<=10))
    return true;
    else return false;
  }

  public void button1_ActionPerformed(ActionEvent evt) {
    double betrag = Double.parseDouble(textField1.getText());
    int i=0, gesamt=0;
    do
    {
      if(betrag>=muenz[0][i])
      {
        anzahl_muenzen[i]=(int)(betrag/muenz[0][i]);
        betrag=betrag%muenz[0][i];
        betrag=(java.lang.Math.round(betrag*100)/100.);
      }
      gesamt=gesamt+anzahl_muenzen[i];
      i++;
      if(i>7) break;
    }while(betrag>0);
    if(gesamt==1)
    label1.setText("Es wird insgesamt 1 Münze benötigt");
    else
    label1.setText("Es werden insgesamt "+((Integer)gesamt).toString()+" Münzen benötigt");
    textArea1.setText(null);
    for(i=0;i<=7;i++)
    {
       if(anzahl_muenzen[i]!=0)
       {
         textArea1.insert((muenz[0][i]+" Euro: "+anzahl_muenzen[i]+"\n"),0);
         if (pruefung(i)==true)textArea1.insert(("ACHTUNG!WENIG VON DEN " + muenz[0][i]+" EURO MÜNZEN VORHANDEN!\n"),0);
       }
    }
    neuberechnen();
  }

  public int abfragen(double muenze) throws Exception
  {
       for(int i=0;i<=7;i++)
       {
         if(muenze==muenz[0][i])
         return (int)muenz[1][i];
       }
       throw new Exception("Eingegebene Münze nicht vorhanden!!");
  }

  public void button2_ActionPerformed(ActionEvent evt) {
     label3.setText("");
     double muenze = Double.parseDouble(textField2.getText());
     try
     {
        label3.setText("Es sind noch "+abfragen(muenze)+" münzen verfügbar");
     }
     catch(Exception fehler)
     {
        System.out.println(fehler);
     }
  }

  // Ende Methoden

  public static void main(String[] args) {
    new Muenzautomat("Muenzautomat");
  }
}
```

Danke

Gruß

schihab


----------



## Ebenius (23. Mrz 2009)

Öhm. Rechtsklick auf die Klasse, "New" auswählen, "JUnit Test Case" auswählen, und dann die Tests implementieren. Viel Spaß.

Ebenius


----------



## schihab (23. Mrz 2009)

danke für die antwort. Hab aber noch nie mit JUnit gearbeitet. Hab gemacht was du gesagt hast und Eclipse hat mir das ausgegeben.


```
public class Muenz {

}
```

Wie muss ich das denn implementieren?


----------



## 0x7F800000 (23. Mrz 2009)

haufen void-methoden die irgendwas am zustand des automaten ändern, und kein einziger getter... :bahnhof: Nja... Da hat sich Ebenius mit *viel Spaß* wirklich diplomatisch aus der Sache gerettet... :toll:

Eigentlich würde ich spontan empfehlen, den code irgendwie so umzuschreiben, dass klar wird was das Interface ist, und was es überhaupt tut. Zum Testen (und für alle andere Sachen auch) wäre es wohl am praktischsten, den Inhalt aus der Graphischen Hülle herauszuschälen und in eine eigenständige klasse auszulagern, die die methoden zum verändern und zum abfragen des Zustandes anbietet.

Um die GUI zu testen, müsste man nur noch ein paar ActionEvents direkt per actionPerformed() an die Components schicken, aund dann aus den anderen components die Ergebnisse auslesen und mit den erwarteten werten vergleichen... Aber so..?

Mal eine Frage nebenbei: wenn du schon mit JUnit rumballern musst, wirst du ja wohl auch was von der for-schleife gehört haben, kannst dann vielleicht mal erzählen, was dieser ganzer kram in "init" und "pruefen" soll?


----------



## schihab (23. Mrz 2009)

Also aufgabe war es einen automaten zu programmieren der 100 von allen euro münzen beinhaltet. Nach eingabe eines bestimmten wertes soll der automat dann berechnen wie er diesen betrag ausgeben muss. Mit der minimalen anzahl an Münzen. init legt die münzen mit jeweils der anzahl 100 an. und pruefen prüft ob weniger als 10 münzen von einer bestimmten münzsorte vorhanden ist damit eventuell eine warnung ausgegeben werden kann.

Wie meint ihr genau mit umschreiben?

Bitte um hilfe

danke im voraus

Gruß Schihab


----------



## 0x7F800000 (23. Mrz 2009)

schihab hat gesagt.:


> Also aufgabe war es einen automaten zu programmieren der 100 von allen euro münzen beinhaltet. Nach eingabe eines bestimmten wertes soll der automat dann berechnen wie er diesen betrag ausgeben muss. Mit der minimalen anzahl an Münzen. init legt die münzen mit jeweils der anzahl 100 an. und pruefen prüft ob weniger als 10 münzen von einer bestimmten münzsorte vorhanden ist damit eventuell eine warnung ausgegeben werden kann.
> 
> Wie meint ihr genau mit umschreiben?


So umschreiben, dass alle diese tollen & nützlichen Informationen, die du da oben in Prosa hingeschrieben hast, sich deutlicher in der Code struktur, in den methodenbezeichnern und in der Kommentaren wiederspiegeln...

Wie soll ich denn zB. mit diesem deinen code feststellen, wie ich 9,87€ in Münzen darstelle, ohne an irgendwelchen Buttons rumzuklicken? 
Wie soll ich feststellen, ob dem Automaten irgendwelche münzen ausgegangen sind, ohne auf irgendwelche textfelder zu guggen? Ich meine: vielleicht bin ich ja ein Cola-automat, der irgendwie rausfinden will, was da mit seinen Münzen los ist... Der hat keinen Bildschirm, der kennt keine Maus, hat keine Algorithmen zum erkennen des Textes in textfeldern, was soll er denn machen mit deinem code? :autsch:

ach ja: "throw Exception"  ... Wozu gibt's maps und if-abfragen?


----------



## schihab (23. Mrz 2009)

Nein meine aufgabe war es eine art "Simulation" eines Automaten zu programmieren. Und das dann mit JUnit zu testen. Und zu deiner frage wegen Exception: das sollte ich so machen. Ich sollte die fehlermeldung mittels eines Exception abfangen.

Und mein Problem ist jetzt der JUnit test.

Brauche dringend deine hilfe

Danke dir

Gruß

Schihab


----------



## 0x7F800000 (23. Mrz 2009)

schihab hat gesagt.:


> Nein meine aufgabe war es eine art "Simulation" eines Automaten zu programmieren. Und das dann mit JUnit zu testen.


Stell dir vor: deine Aufgabe wäre es, eine mechanische Puppe zu bauen, die bestimmte Anordnungen aus Holzklötzen stapeln soll. (entspricht dem Programmieren der Klasse) 

Jetzt ist deine Aufgabe, ein paar interessante Anordnungen auszudenken, um zu schauen, ob die Puppe diese stapeln kann. (das entspricht dem JUnit)

Welche Holzklotzanordnungen sind zum testen deiner Puppe gut geeignet, wenn sie keine Augen und keine Arme hat, sondern nur theoretisch etwas vom Holzklotzstapeln versteht, und mit ihrer Umwelt ausschließlich durch Gerüche kommunizieren kann? Ich weiß es jedenfalls nicht. 

(entspricht deiner implementierung: die informationen über die GUI auszutauschen ist zwar etwas einfacher, als über die Gerüche, aber ein Test der das bewerkstelligen würde, wäre etwa drei mal so lang wie das Programm selbst, übertragen auf die Metapher wäre das etwa dasselbe wie der Bau eines Gaschromatographen mit Roboterarmen)

Jetzt mal ehrlich, was sollen denn Leute von einem Münzautomaten denken, der die Methode *void* *button2_ActionPerformed(ActionEvent e)* zur Verfügung stellt?! 
Du kannst mal zum ADAC gehen und fragen, ob sie nicht ein Auto mit der öffentlichen methode *void fingerInsGetriebeStecken(Finger f) *testen wollen :noe:

=> Trenn die GUI vom Automaten. Es sollen definitiv keine Berechnungen in den "actionPerformed"-methoden stattfinden. So wie es jetzt ist, kann man das weder benutzen noch testen.



> Und zu deiner frage wegen Exception: das sollte ich so machen. Ich sollte die fehlermeldung mittels eines Exception abfangen.


okay, für's erste gut genug, nicht das Hauptproblem.


----------



## schihab (23. Mrz 2009)

Also ich glaube fürs erste hab ich verstanden was du meinst. Wie wärs wenn ich die berechnungen und alles was über den Knopf aufgerufen wird in einer eigenen funktion steht?! meinst du doch auch so? oder?

Aber die ausgabe auf den textfeldern und so weiter kann ich doch lassen. oder?

danke


----------



## 0x7F800000 (23. Mrz 2009)

Ja, die berechnungen sollen alle in separaten methoden der Münzautomaten-klasse stattfinden. Wenn die berechnungen öffentlich sein sollen, deklarierst du sie public, alle hilfsmethoden bleiben private. diese ganzen actionperformed-methoden sollten mindestens in etwa so aussehen:
[highlight=Java]
public void actionPerformed(){
   argument = Whatever.parseSomehow(textfieldX.getText());
   result=muenzautomat.getXYZResultForSomeCalculation(argument);
   textfeldY.setText(result);
}
[/highlight]

dann wäre die funktionalität des Münzautomaten in methoden wie etwa getXYZResultForSomeCalculation() gekapselt, diese könntest du dann jeweils im JUnit einfach testen, ohne den irrsinnigen Umweg über die GUI.


----------



## schihab (23. Mrz 2009)

alles klar das werde ich jetzt machen. Und wie mache ich dann die tests? Also wie genau teste ich die klasse und die einzelnen methoden?


----------



## Ark (23. Mrz 2009)

schihab hat gesagt.:


> Nach eingabe eines bestimmten wertes soll der automat dann berechnen wie er diesen betrag ausgeben muss. Mit der minimalen anzahl an Münzen.


Gehört dieses Problem nicht zu einem von diesen hier? 






Möglicherweise ist die Verteilung der Euro-Münzen aber gerade so gewählt, dass greedy immer optimal ist.

Ark


----------



## schihab (24. Mrz 2009)

hi nochmals,

hab jetzt die gui getrennt. hab nur noch ein problem. Wie mache ich das mit JUnit? Wie kann ich die tests machen?

Bitte um hilfe

danke

gruß

schihab


----------



## 0x7F800000 (24. Mrz 2009)

schihab hat gesagt.:


> hab jetzt die gui getrennt. hab nur noch ein problem. Wie mache ich das mit JUnit? Wie kann ich die tests machen?


Okay. Da die nervige GUI jetzt vom Automaten getrennt ist, und der Automat endlich mal ein *Unit* darstellt, könnte man damit endlich mal *Unit-Tests* machen 

So ein Unit-Test könnte in etwa so aussehen:
[highlight=Java]
import static org.junit.Assert.*;
import org.junit.*;

public class MuenzAutomatTest {

    MuenzAutomat automat;

    @Before
    public void setUp(){
        //automat irgendwie sinnvoll initialisieren:
        automat= new MuenzAutomat(new int[]{100,100,200,300,500,500,1000,1000});
        //zum beispiel mit 100x2€ Münzen,100x1€ Münzen, .... , 1000x1cent Münzen usw. 
        //hängt halt davon ab, wie sich dein automat initialisieren lässt
    }

    @After
   public void tearDown(){
      automat=null; /zwar eher unsinn, ist halt nur nur um hier irgendwas zu schreiben...
   }

   @Test
   public void abfragenTest(){
       //hier könnte man zum beispiel guggen, ob der automat auch mitgekriegt hat, dass er 500x5cent münzen zur verfügung hat
        assertEquals(500,automat.abfragen("5");
   }

   //wie die "Exception"-Exception zu handhaben ist: keine Ahnung
   @Test(expected=Exception.class) //wäre ja wohl extrem sinnfrei ()???
   public void abfragenExceptionTest(){
        ????
   }

   //guggen, ob der was korrektes berechnet, dieser dein automat...
   @Test
   public void geldbetragInMuenzenDarstellenTest(){
       //am beispiel 5.87€ = 2x2+1+0.50+0.20+0.10+0.5+0.02+0x0.01 
       assertEquals(new int[]{2,1,1,1,1,1,1,0}, automat.inMuenzenDarstellen(587));
      ...
   }

   //99 mal nach 2 euro fragen, schauen ob richtige warnungen rauskommen
   ...
   //alle großen münzen entfernen, schauen ob's immer noch funzt...
   ...
   //sonst alles checken halt...
}
[/highlight]
Steht ja eigentlich alles hier: JUnit 4.x Howto
ist ja keine große sache das grundprinzip zu raffen, zu viele neue Annotationen kommen da auch nicht... viel Spaß.:toll:


----------



## didjitalist (25. Mrz 2009)

solche aufgaben lösen sich am besten, wenn man den unit test zuerst schreibt und dann erst die implementierung.


----------



## 0x7F800000 (25. Mrz 2009)

...Bzw sich zuerst ein sinnvolles interface überlegt, dann den unit test schreibt, und dann das leere Skelett ausfüllt?


----------

