# Hibernate -> Probleme bei n:m-Relation und JUnit



## PHANTOMIAS (8. Jun 2010)

Hallo an alle!

Ich habe eine Adobe Flex <- BlazeDS -> J2EE Anwendung mit einer Hibernate / MySQL Anbindung.

Nun habe ich zwei Tabellen: Projekt und Benutzer, die n:m in einer Zwischentabelle verknüpft sind: Projekt_Benutzer mit projektId und benutzerId.

In der Klasse Projekt ist eine List von benutzern, in der Klasse Person eine Liste von Projekten mit entsprechenden getter/setter-Methoden. Ich habe also eine bidirektionale Verbindung.

Nun habe ich aber zwei "Probleme":

Wie füge ich einen Benutzer einem Projekt hinzu? Schreibe ich dafür bei der Schnittstelle des Benutzers eine Methode addBenutzerToProject(Benutzer benutzer, int projektId)? Oder mace ich das in dem ich derArrayList von Projekten beim Benutzer eine add-Methode aufnehme auf Seiten des Frontend und ich dann ein updateBenutzer() aufrufe? Ich hoffe ihr versteht was ich meine... Und da ich das bidirektional habe, kann ich ja auch das Gleiche auf Seiten des Projekts machen. Da ist auch eine Liste von Benutzern, die müsste ich erweitern und dann das Projekt updaten. Wie geht man da i.A. vor?
Ich schreibe JUnit-Tests. Nun habe ich je eine Schnittstelle für Projekt und Benutzer mit den Methoden: add, update, remove, getAll, findById. Nun kann ich diese mit einer JUnit-Testklasse ausführen lassen in der Art:


```
IBenutzerService s = new BenutzerServiceImplementierung();
@Test
public void testAddBenutzer() {
    testRemoveBenutzer();
    for (int i=0; i < 5; i++) {
        Benutzer b = new Benutzer();
        // setter-Methoden
        s.addBenutzer(b);
    }
}
// oder andere Methoden mit assertEquals-Abfrage..
So habe ich für eine Klasse, eine JUnit-Klasse. Wie kann ich denn aber nun die Zwischentabelle "Projekt_Benutzer" testen lassen? Dann muss ich ja erst Projekte anlegen lassen, und dann Benutzer, und die dann verbinden. Schreibe ich dann eine JUnit-Klasse, die dann beide Interface-Services verbindet? Oder rufe ich die Test-Methoden aus beiden Testklassen auf?
```

Ich hoffe jemand kann mir dazu etwas sagen, da ich daran hänge und ich nicht weiß wie ich das am Besten tun soll.

Danke + Gruß PHANTOMIAS


----------



## maki (8. Jun 2010)

Dein Test ist schlecht, weil er von anderen Tests abhängt, sieh dir mal DBUnit an.

Wie du Benutzer speichern möchtest solltest du eigentlich wissen


----------



## PHANTOMIAS (8. Jun 2010)

Über DBUnit bin ich auch schon gestolpert, nur ist mir unklar, jetzt mal unabhängig von einer Datenbank, wie ich Unit-Tests schreibe, die sich nicht untereinander "helfen"?

Ich meine bei mir ist es nicht allzu problematisch, da ich die DB jedes Mal neu aufsetze und läuft was schief, dann läuft was schief; ich korrigiere es, bis es durchläuft 

DBUnit bringt dann vorm Testlauf die DB auf einen bestimmten Stand, richtig? Also bspw. beim Anlegen von neuen Benutzern, statt meine testRemoveBenutzer() aufzurufen setzt er die DB so, dass keine Benutzer in der Tabelle enthalten sind, richtig? So baue ich dann nicht auf diese andere test-Methode auf.

Ja, ich denke ich füge die Projekte zu den Benutzern im Frontend zu und biete da eine Methode um den Benutzer das Projekt hinzuzufügen lassen. Und dann kann ich ja ein updateBenutzer() anstoßen, dann wird das ja automatisch mit gesetzt, oder? Ich mache da ein "session.update(benutzer)" in der Hibernate Implementierungsklasse des Benutzers.

Aber wie schreibe ich dann nach JUnit-Manier (mal unabhängig vom Aufbau auf andere Tests) den Test dass ich ein Projekt anlege, dann einen Benutzer und diese dann verbinden lasse?
Lege ich da eine ProjektBenutzerTest-Klasse an, instanziiere beide Service-Implementierungen? Aber ich wähle doch immer nur eine Klasse aus, die getestet werden soll und kann da dann mit meineMethode() testMeineMethode() notieren lassen. Bin da etwas verwirrt wie ich also klassenübergreifend testen lasse...

Danke + Gruß PHANTOMIAS


----------



## maki (8. Jun 2010)

> Über DBUnit bin ich auch schon gestolpert, nur ist mir unklar, jetzt mal unabhängig von einer Datenbank, wie ich Unit-Tests schreibe, die sich nicht untereinander "helfen"?


Die Antwort hast du dir schon selber gegeben:


> DBUnit bringt dann vorm Testlauf die DB auf einen bestimmten Stand, richtig? Also bspw. beim Anlegen von neuen Benutzern, statt meine testRemoveBenutzer() aufzurufen setzt er die DB so, dass keine Benutzer in der Tabelle enthalten sind, richtig? So baue ich dann nicht auf diese andere test-Methode auf.





> Ich meine bei mir ist es nicht allzu problematisch, da ich die DB jedes Mal neu aufsetze und läuft was schief, dann läuft was schief; ich korrigiere es, bis es durchläuft


Da täuscht du dich, Grundregel für Tests: Keine Abhängigkeiten/Interaktion zwischen tests.
Im Prinzip könnte sogar jeder einzelne Test die DB neu aufsetzen...



> Aber wie schreibe ich dann nach JUnit-Manier (mal unabhängig vom Aufbau auf andere Tests) den Test dass ich ein Projekt anlege, dann einen Benutzer und diese dann verbinden lasse?
> Lege ich da eine ProjektBenutzerTest-Klasse an, instanziiere beide Service-Implementierungen? Aber ich wähle doch immer nur eine Klasse aus, die getestet werden soll und kann da dann mit meineMethode() testMeineMethode() notieren lassen. Bin da etwas verwirrt wie ich also klassenübergreifend testen lasse...


Solltest dir immer genau darüber bewust sein, was du testen willst.
Was meinst du mit"Klassenübergreifend"?
Willst du isolierte Unittestests (also ohne echte DB), oder Integrationstests (mit DB), oder gar Akzeptanztests(ganze Abläufe)?
Die Komplexität steigt übrigens in dieser Reihenfolge an, und zwar deutlich


----------



## PHANTOMIAS (8. Jun 2010)

Hmm, du merkst, ich bin noch test-unerfahren. Ich muss das Testen erst testen 

Aber eigentlich hätte ich gerne in meinem Fall eigentlich gern den folgenden Ablauf des Tests:

add 10 Benutzer
update die ersten fünf Benutzer
remove 2-3 Benutzer
getAll Benutzer
findBenutzerById
-> So bräuchte ich doch keinen Aufbau auf andere Test-Methoden?! Also ich rufe zumindest mal in keiner Testmethode eine andere Testmethode auf.

Integrationstests mache ich ja zur Zeit mit der Datenbank. Ja, was ich gerne möchte ist wohl ein Akzeptanztest (bin noch nicht 100%ig mit den Begrifflichkeiten vertraut). Also ich will gerne simulieren:
Anlage eines Projekts, Anlage eines Benutzers, Verbinden Projekt<->Benutzer.
Bedeutet: Erst addProjekt(), dann addBenutzer(), und dann der Test  einmal auf der Seite des Projekts, in dem ich die Liste von Benutzer erweitere, und das andere Mal (zweiter Test), dass ich bei den Benutzern, die Liste der Projekte erweitere. Und dann ein update() auf Projekt bzw. Benutzer.
Und das sollte ich ja dann in der Datenbank in der Zwischentabelle Projekt_Benutzer sehen können?!

Gruß PHANTOMIAS


----------



## maki (8. Jun 2010)

Solltest dir wirklich, wirklich im klaren sein was du testen willst 

Alles auf einmal testen ist komplex, und schlecht für die Defektlokalisierung, und meist auch langsam 

Würde an deiner Stelle langsam anfangen, teste mal isolierte Einheiten(Unittests), ohne DB, lerne Mockobjekte nutzen usw.


----------



## PHANTOMIAS (8. Jun 2010)

So viele Defekte kann ich gar nicht haben, das ist nur ein Testprojekt mit 3 Datenbanktabellen und insgesamt 12 Spalten. Es geht mir nur darum mit Hibernate umgehen zu lernen und mit Testen.

Also ich habe wirklich ein kleines System. Das mit Projekt und Benutzer ist schon das absolute Highlight mit n:m. Die andere Relation ist noch 1:n zum Testen, das wars. Außer getter/setter habe ich auch nichts in den Klassen. Und getter/setter teste ich wohl eher nicht. Somit habe ich keine reinen Unittests durchzuführen, nur in Kombination mit der Datenbank. Und bisher bin ich eigentlich zufrieden: Datenbank wird korrekt angelegt und ich kann einzeln testen. DBUnit schaue ich mir gleich noch näher an und versuche das zu integrieren, dass ich nicht andere Test-Methoden aufrufen muss.

Aber nur zu meinem Verständnis: wäre es kein workaround, dass ich die test-Methoden in einer bestimmten Reihenfolge aufrufe, wie im vorherigen Post von mir beschrieben? Dann bräuchte ich nicht innerhalb von Testmethoden auf andere Testmethoden zugreifen. Kann man nicht eine bestimmte Reihenfolge festlegen der Methoden wie sie aufgerufen werden?

Nun, aber wie tue ich denn nun diesen Akzeptanztest implementieren? Ich habe ja nur einen: Projekt anlegen, Benutzer anlegen, Projekt-Benutzer-Beziehung herstellen. Weitere sinnvolle Abläufe habe ich nicht in meinem Testprojekt, da der Rest nur setzen und holen ist.
Ich weiß nicht ob es dafür etwas übertrieben ist, mit Dummy-Objekten zu arbeiten?!

Gruß PHANTOMIAS


----------



## maki (8. Jun 2010)

Tests von einander abhängig zu machen ist nie eine gute Idee, bei Akzeptanstests allerdings macht man das oft so, dass eine TestSuite erstellt wird, die Testcases in einer bestimmten Reihenfolge luafen lässt.

Für alle anderen Arten von Tests gilt aber: Keine Abhängigkeiten zwischen Tests!

Nicht umsonst erstellen alle xUnit Frameworks (mit einer einzigen Ausnahme, der NUnit Author hat den Fehler eingesehen sich dafür entschuldigt) erstellen für jede Testmethode (=TestCase) eine eigene Instanz der Testklasse.

Ich denke du würdest dich übernehmen wenn du in diesem Stadium Integrationstests schreibst, lernen würdest du nix  sinnvolles/vernünftiges imho, ausser höchstens Hacks/Workarounds/schlechte Angewohnheiten.

Nebenbei, Mocks sind keine Dummys: Mocks Aren't Stubs

Mocks gehören übrigens zur Standardausrüstung für isolierte Unittests, dass  sind die Tests die du zuerst lernen solltest.

Nachtrag: Wer schon mal mit abhängiggen tests zutun hatte, weiss worum es geht: 
Mal gehen die Tests, mal nicht, dann gehen sie wieder, aber jedesmal mit einem anderen Fehler bzw. anderen test der fehlschlägt, und das alles ohne Änderungen am Code


----------



## PHANTOMIAS (8. Jun 2010)

Okay-okay, dann höre ich wohl mal lieber auf dich 

Zwei letzte Fragen:

Gibt es ein gutes Buch darüber wo man das von Grund auf lernen kann?
Ich schreibe zur Zeit immer einzelne Testklassen und führe die mit run as -> JUnit test aus. So muss ich jedesmal das einzeln für jede Klasse initiieren. Kann man das vereinen?
Und nun gehe ich mal zum DBUnit über...

Danke + Gruß PHANTOMIAS


----------



## maki (8. Jun 2010)

Bücher für den Einstieg kenne ich keine mehr, kann "XUnit Test Patters - Refactoring Test code" empfehlen, ist aber was für Fortgeschrittene.

Hier ein Besipiel für DBUnit von der XUnit Webseite (Example: Back Door Fixture Setup ) : Back Door Manipulation at XUnitPatterns.com

TestSuiten kann man sich dynamisch erstellen lassen, zB. von Ant, Maven2, Eclipse (letzteres hat ein Problem mit JUnit 4 Tests).


----------



## PHANTOMIAS (8. Jun 2010)

Okay, werde dann mal nach Literatur schauen...

Mist, Maven2 konnte ich nicht in mein kombiniertes Flex/Java-Projekt einbauen. Und Eclipse hat Probleme mit JUnit4, das ich nutze, das ist doof


----------

