JUnit - Mini-Test

Blues

Mitglied
Hallo zusammen,

ich beschäftige mich seit kurzem zum allerersten Mal mit JUnit und bin leider verzweifelt. Ich habe mich mit einigen Anleitungen im I-Net beschäftigt und viel rumprobiert, trotzdem weiß ich immer noch nicht genau, wie ich zu dem folgenden Code eine passende JUnit Klasse schreiben kann/muss. Hab echt keine Ahnung mehr, wie ich da weiter vorgehen soll, wäre wirklich spitze, wenn mir wer dabei weiterhelfen könnte.

Also ich würde gerne diese Klasse hier testen. Es geht darum, dass Studenten, die mehr als 4 Semester studieren auf eine Attraktion pro Semester 1 Euro Nachlass bekommen (das ganze wird dann später noch erweitert, ist mir so aber erstmal vorgegeben).

[Java]
package file;
public class Studentenpreis extends Preis {

@Override
double getNachlass(int studiendauer) {

double result = 5;
if (studiendauer > 4) {
result -= (studiendauer - 3) * 1;
}
return result;
}

@Override
int getPreiskategorie() {

return Movie.STUDENTEN;
}

}
[/Java]

Hier nun mein Versuch einer Testklasse. Was ich mir dazu überlegt habe:
- Zunächste habe ich eine Start Methode, die die Methoden setUp(), runTest() und teardown() aufruft. Diese drei Methoden sind mir so grundsätzlich vorgegeben
- Von der Idee her habe ich mir dann gedacht, soll in der setUp() Methode erstmal alles erzeugt werden, was man zum Testen braucht, ich dachte da an eine Instanz von Studentenpreis
- Dann runTest() damit was testen. Daher übergebe ich die erzeugte Instanz.
- Dort wird wiederum die Instanz an die verschiedenen Methoden weitergegeben, hier ist es ja nur die eine
- Dort möchte ich dann ja irgendwas testen, mir ist als einzig Sinnvolles leider nur eingefallen, dass die Studiendauer nicht null sein darf.
- Danach würde in teardown() dann die Instanz gelöscht werden.

Ich habe vermutlich ziemlichen Spaghetticode produziert, da ich mir mehrere Tutorials angeschaut habe und leider jedes komplett anders vorgeht. Das unten ist daher auch eher als Pseudocode zu verstehen.

Es handelt sich um ein ganzes Programm mit einigen Klassen, ich bin bereit, mich da rein zu arbeiten, brauche aber einfach mal ein Beispiel, wie das aussehen kann. Wäre daher wirklich sehr dankbar, wenn Jemand meine Testklasse entsprechend anpassen könnte... weiß nicht weiter...

Java:
package test;

import junit.framework.Assert;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.junit.Test;
import junit.framework.TestResult;

import org.junit.Test;

import file.Studentenpreis;

public class StudentenpreisTest extends TestCase{
	
	Studentenpreis studentenpreis;
	
	@ Test
	public void run (TestResult result) {
		result.startTest(this);
		setUp();
		
		try {
		runTest(studentenpreis);
		} catch (AssertionFailedError e1) {
			result.addFailure(this, e1);
		} catch(Throwable e2) {
			result.addError (this, e2);
		} finally {
		}
		tearDown(studentenpreis);
	}
	
	@ Test
	protected setUp() {
		Studentenpreis studentenpreis = new Studentenpreis ();
		return;
	};
	
	@ Test
	protected void runTest() {
		testGetNachlass(studentenpreis);
	};
	
	@ Test
	protected void tearDown() {

	};
	
	@Test
	public void testGetNachlass() {
		Assert.assertTrue(!studentenpreis.equals(null));
	}

	

}
 
Erst mal ein bischen Theorie:
Es geht darum, dass Studenten, die mehr als 4 Semester studieren auf eine Attraktion pro Semester 1 Euro Nachlass bekommen.
Das ist die fachliche Beschreibung dessen, was die Methode machen soll. Die erste Frage, die Du anhand dieser Beschreibung beantworten musst, ist: Was für Fälle gibt es hier und was wird zu jedem Fall erwartet? Eine Anwort könnte sein:
  1. Werte kleiner 0 --> IllegalArgumentException schmeißen (es gibt keine negativen Studiendauern)
  2. Werte 0 bis 4: 0 Muss rauskommen, der Student erhält auf keine Attraktionen den Euro Nachlass
  3. Werte größer 4: Pro Semester größer 4 muss eine Attraktion mehr herauskommen (also 1 bei 5, 2 bei 6 usw.)
  4. Werte größer 20: --> Student wird Zwangsexmatrikuliert 😀
Mit dieser Fragestelltung und den zugehörigen Antworten siehst Du bereits ohne Tests, dass Deine Implementierung im Sinne der Anforderung fehlerhaft ist. Sie gibt nämlich garnicht die Anzahl der Attraktionen aus, für die ein Student den Euro Nachlass bekommt, sondern versucht offensichtlich, einen Rabatt in Euros zu errechnen. Aber selbst das tut sie nicht korrekt (rechne das mal mit einer Studiendauer von 9 durch). Also: Nochmal hinsetzen und neue Methode schreiben!
 
Hey,

sorry, vllt. habe ich mich falsch ausgedrückt:

Also die obige METHODE ist mir so vorgegeben, die Umschreibung habe ich mir daraus selbst erschlossen, da es keine Beschreibung gibt. Das heißt, dass ich die Methode falsch gelesen habe (vielen Dank dafür, macht natürlich auch Sinn!). An ihr ändert sich daher nichts. 😉
 
Und nun zum JUnit-Test. Fang erstmal ganz einfach an, lass den ganzen setUp-/tearDown/run-Kram weg. Das kannst Du für den Anfang komplett in der Testmethode abhandeln. Die könnte dann mit den Erkenntnissen aus meinem letzten Post so aussehen:
Java:
@Test
public final void testGetNachlass() {

  final Studentenpreis testObjekt = new Studentenpreis();

  int nachlass;

  try {
      testObjekt.getNachlass(-1);

      Assert.fail("IllegalArgumentException hätte fliegen sollen!");
  }
  catch(IllegalArgumentException e) {
     // tu nix, Test war erfolgreich.
  }

  nachlass = testObjekt.getNachlass(0);

  Assert.assertEquals(0, nachlass);

  // Hier testen wir die Grenze 4.
  // Grenzfälle testen ist wichtig, weil Programmierer
  // gerne mal mangels Übersicht < statt <= oder > statt >= schreiben
  // Der Test entlarvt das.
  nachlass = testObjekt.getNachlass(4);

  Assert.assertEquals(0, nachlass);

  ....
}
Das Schema ist immer, Methode mit Testdaten ausführen und Ergebnis in einer Variablen speichern. Danach mit Assert.assertXXX überprüfen, ob Deine Erwartung erfüllt ist. Von den assertXXX-Methoden gibt es eine ganze Menge, fast eine für jeden denkbaren Fall. So ein Krautkram ist darum nicht nötig:
Java:
    // Schlecher Methodenname, sie heißt testGetNachlass,
    // aber testet equals()
    @Test
    public void testGetNachlass() {
        // assertTrue und dann Ergebnis negieren, schlecht lesbar
        Assert.assertTrue(!studentenpreis.equals(null));
       // Besser
       Assert.assertFalse(studentenpreis.equals(null));
    }
[EDIT]Habe Deinen letzten Post erst gelesen, nachdem ich mit dem hier fertig war, habe meinen Code nun nochmal entsprechend angepasst[/EDIT]
 
Zuletzt bearbeitet von einem Moderator:
Also die obige METHODE ist mir so vorgegeben,
Java:
    @Override
    double getNachlass(int studiendauer) {
 
        double result = 5;
        if (studiendauer > 4) {
            result -= (studiendauer - 3) * 1;
        }
        return result;
    }
Die Methode ist Dir so vorgegeben? Die ist doch fehlerhaft! Oder ist das Absicht, damit Deine JUnit-Tests die Fehler finden?

Ohne eine Beschreibung dessen, was die Methode tun soll macht eine Aufgabe für meinen Geschmack wenig Sinn. Man kann so die Testfälle überhaupt nicht definieren. Anhand einer fehlerhaften Implementierung aus dem Code zu schließen, was der Code eigentlich tun sollte, genzt jedenfalls an magische Fähigkeiten.
 
Zuletzt bearbeitet von einem Moderator:
- Zunächste habe ich eine Start Methode, die die Methoden setUp(), runTest() und teardown() aufruft.
Brauchst Du nicht. In JUnit gibt es sog. Testrunner. Die übernehmen den Aufruf der Methoden in der richtigen Reihenfolge für Dich. Deine Aufgabe als JUnit-Nutzer ist nur, die Methoden zu schreiben (wenn Du sie brauchst) und sie korrekt mit Annotationen zu versehen.
- Von der Idee her habe ich mir dann gedacht, soll in der setUp() Methode erstmal alles erzeugt werden, was man zum Testen braucht, ich dachte da an eine Instanz von Studentenpreis
Das ist richtig gedacht. Macht man so.
- Dann runTest() damit was testen. Daher übergebe ich die erzeugte Instanz.
Weg damit! JUnit-Tests werden von Testrunnern ausgeführt. Die gibt es schon.
Dort möchte ich dann ja irgendwas testen, mir ist als einzig Sinnvolles leider nur eingefallen, dass die Studiendauer nicht null sein darf.
Blödsinn! "studiendauer" ist ein int. Das kann garnicht null werden.
Danach würde in teardown() dann die Instanz gelöscht werden.
Da kann man hier nicht viel mehr machen, als die Variable "null" zu setzen. Macht aber nicht wirklich Sinn.
 
Hi nillehammer,

ja, so wie ich das jetzt rausgelesen habe, sollen die Tests auch Fehler im Code aufdecken, die sollen aber nicht korrigiert werden...

Jetzt habe ich aber noch eine Frage zu deinem Testfall. Ich hab diesen also soweit angepasst. Würde dieser Ausschnitt so passen? Sprich bei jedem Test ein try und catch Block, oder?

[Java]
@Test
public final void testGetNachlass() {

final Studentenpreis testObjekt = new Studentenpreis();

int nachlass;

try {
testObjekt.getNachlass(-1);
Assert.fail("IllegalArgumentException hätte fliegen sollen!");
}
catch(IllegalArgumentException e) {
// tu nix, Test war erfolgreich.
}

try {
nachlass = testObjekt.getNachlass(0);
Assert.assertEquals(0, nachlass);
}
catch(IllegalArgumentException e1) {
Assert.fail("IllegalArgumentException hätte fliegen sollen!");
}

// Hier testen wir die Grenze 4.
// Grenzfälle testen ist wichtig, weil Programmierer
// gerne mal mangels Übersicht < statt <= oder > statt >= schreiben
// Der Test entlarvt das.

try {
nachlass = testObjekt.getNachlass(4);
Assert.assertEquals(0, nachlass);
}
catch(IllegalArgumentException e2) {
Assert.fail("IllegalArgumentException hätte fliegen sollen!");
}
}
}
[/Java]
 
Zuletzt bearbeitet:
Hey,

jetzt muss ich trotzdem nochmal nachfragen. Habe meine Testklasse jetzt erstmal nach deinem ersten Beispiel umgeschrieben. Die Testklasse liefert nun 'rot', also liegt doch ein Fehler vor. Bei Failure Trace bekomme ich diese Meldung:

junit.framework.AssertionFailedError: IllegalArgumentException hätte fliegen sollen!

D.h. doch, dass der Test -1 übergeben hat. Die Testklasse sagt doch 'wenn ich -1 übergebe, ist das nicht richtig', der Klasse ist das aber egal. Nun muss ich die Klasse so anpassen, dass -1 nicht übergeben werden darf, oder? Muss ich in der Testklasse dann auch Fälle mit -2, -3 etc. berücksichtigen?

Viele Grüße
 
etzt habe ich aber noch eine Frage zu deinem Testfall. Ich hab diesen also soweit angepasst. Würde dieser Ausschnitt so passen? Sprich bei jedem Test ein try und catch Block, oder?
Nein! Das mit dem try-catch machst Du natürlich nur, wo Du erwartest, dass eine Exception fliegen soll. Bei allen anderen natürlich nicht. Habe ich deswegen in meinem Beispielcode auch nicht gemacht.

Also nochmal zum Vorgehen: Du rufst Deine Methode mit Testdaten auf. Du hast dabei bestimmte Erwartungen an das Ergebnis. Wenn das erwartete Ergebnis ein Wert ist, überprüfst Du, ob der Wert passt. Wenn das erwartete "Ergebnis" eine Exception ist, prüfst Du ob sie auch wirklich geworfen wurde.
 
Zuletzt bearbeitet von einem Moderator:
D.h. doch, dass der Test -1 übergeben hat. Die Testklasse sagt doch 'wenn ich -1 übergebe, ist das nicht richtig', der Klasse ist das aber egal. Nun muss ich die Klasse so anpassen, dass -1 nicht übergeben werden darf, oder? Muss ich in der Testklasse dann auch Fälle mit -2, -3 etc. berücksichtigen?
In der Regel musst Du das nicht. Bei Unit-Tests ist es wichtig, dass durch die diversen Tests möglichst alle Zweige des zu testenden Codes durchlaufen werden. Der ungültige Fall (studiendauer < 0) ist mit -1 durchlaufen. Tests mit -2, -3 sind dann nicht mehr nötig.
 

Neue Themen


Zurück
Oben