Java JUNIT Tests + Hibernate + TearDown/SetUp

Osiris1

Lt. Junior Grade
Registriert
Feb. 2008
Beiträge
351
Hi

Ok folgendes Problem:
Ich versuche gerade eine In Memory Datenbank (HSQLDB/Hibernate) zu testen.
Dazu schreibe ich meine Testklassen und einen Basistest. Die Testklassen werden vom Basistest abgeleitet. Im Basistest stehen die SetUpBeforeClass und die TearDownAfterClass.

Setup: Datensätze werden eingespielt

TearDown:
Code:
final EntityManagerFactory emf = Persistence
				.createEntityManagerFactory("webres");
		
		EntityManager em = emf.createEntityManager();
		
		em.getTransaction().begin();
		em.createNativeQuery("TRUNCATE SCHEMA PUBLIC RESTART IDENTITY AND COMMIT NO CHECK").executeUpdate();
		em.getTransaction().commit();
		
		em.close();
		emf.close();

Die beiden werden vor und nach jeder Testklasse ausgeführt.
Allerdings führt der Truncate Befehl zu folgendem Fehler:

Die Entitys: Patient und Admin leiten sich beide von User ab.
Die erste Testklasse (AdminDao Test) wird normal ausgeführt.
Die zweite Testklasse (PatientDao Test) kann keine Objekte (Patienten) in der Datenbank mehr anlegen. Alleine funktioniert die Testklasse aber.

Die Exception sieht wie folgt aus:
Code:
javax.persistence.RollbackException: Exception [EclipseLink-26] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Trying to get value for instance variable [exercises] of type [java.util.Set] from the object [webres.entity.Admin].  The specified object is not an instance of the class or interface declaring the underlying field.
Internal Exception: java.lang.IllegalArgumentException: Can not set java.util.Set field webres.entity.Patient.exercises to webres.entity.Admin
Mapping: org.eclipse.persistence.mappings.OneToManyMapping[exercises]
Descriptor: RelationalDescriptor(webres.entity.Patient --> [DatabaseTable(user)])
	at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
	at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
	at webres.dao.GenericDaoJpa.commit(GenericDaoJpa.java:65)
	at webres.dao.GenericDaoJpa.commitAndCloseTransaction(GenericDaoJpa.java:95)
	at dao.TestPatientDao.createPatient(TestPatientDao.java:50)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: Exception [EclipseLink-26] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Trying to get value for instance variable [exercises] of type [java.util.Set] from the object [webres.entity.Admin].  The specified object is not an instance of the class or interface declaring the underlying field.
Internal Exception: java.lang.IllegalArgumentException: Can not set java.util.Set field webres.entity.Patient.exercises to webres.entity.Admin
Mapping: org.eclipse.persistence.mappings.OneToManyMapping[exercises]
Descriptor: RelationalDescriptor(webres.entity.Patient --> [DatabaseTable(user)])
	at org.eclipse.persistence.exceptions.DescriptorException.illegalArgumentWhileGettingValueThruInstanceVariableAccessor(DescriptorException.java:645)
	at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.getAttributeValueFromObject(InstanceVariableAttributeAccessor.java:79)
	at org.eclipse.persistence.mappings.DatabaseMapping.getAttributeValueFromObject(DatabaseMapping.java:574)
	at org.eclipse.persistence.mappings.ForeignReferenceMapping.getAttributeValueFromObject(ForeignReferenceMapping.java:910)
	at org.eclipse.persistence.mappings.DatabaseMapping.getRealAttributeValueFromObject(DatabaseMapping.java:727)
	at org.eclipse.persistence.mappings.CollectionMapping.getRealCollectionAttributeValueFromObject(CollectionMapping.java:1080)
	at org.eclipse.persistence.mappings.CollectionMapping.mergeIntoObject(CollectionMapping.java:1459)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeIntoObject(ObjectBuilder.java:3544)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeChangesIntoObject(ObjectBuilder.java:3477)
	at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfWorkingCopyIntoOriginal(MergeManager.java:878)
	at org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfWorkingCopyIntoOriginal(MergeManager.java:687)
	at org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:307)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeChangesIntoParent(UnitOfWorkImpl.java:3260)
	at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.mergeChangesIntoParent(RepeatableWriteUnitOfWork.java:369)
	at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:283)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1147)
	at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:84)
	... 31 more
Caused by: java.lang.IllegalArgumentException: Can not set java.util.Set field webres.entity.Patient.exercises to webres.entity.Admin
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164)
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168)
	at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:55)
	at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
	at java.lang.reflect.Field.get(Field.java:372)
	at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.getAttributeValueFromObject(InstanceVariableAttributeAccessor.java:76)
	... 46 more
Sry für die wall of text...

Ich werde aus der Fehlermeldung nicht schlau... Und der Truncate Befehl sollte doch nur alle Tables leeren und die ID counter zurücksetzen. Was tut dieser Befehl noch, was ich übersehe? Leider sind Hibernate und In Memory Datenbanken eine ziemliche Blackbox für mich... die Exception sieht irgendwie nach einem Fehler in den Entitys aus, aber da der Patiententest alleine funktioniert (und auch funktioniert wenn ich den Truncate Befehl auskommentiere), bin ich etwas ratlos...

Danke im Voraus
Osiris
 
Zuletzt bearbeitet:
Im Allgemeinen würd ichs so wohl nicht machen.
Bin mir auch nicht ganz sicher ob du nicht unter umständen noch instanzen wo hast, danach das ding von hand löscht und er dann damit nicht klar kommt wenn du die Objecte verwendest. Müsste aber mehr code sehen um dazu was sagen zu können.
 
Danke für die Antwort!
Hm... bis jetzt habe ich das Problem nur bei meinen drei Personen-Entitys: Admin, Therapist, Patient die sich von User ableiten... Die anderen Entitys lassen sich dadurch nicht stören.

Ich bin mir nicht sicher ob ich da wirklich mehr Code posten kann, da ich (glaube ich) eigentlich keine Rechte an dem Projekt habe. Um welchen Code würde es genau gehen? Und von welchen Instanzen sprichst du?

Gibts andere Möglichkeiten die Datenbank vor jeder Klasse neu aufzubauen oder zu leeren ohne das Framework umzustellen?
 
Nunja in der Regel sollte sich ja ein Testfall selbst aufräumen.
Das was er anlegt entfernt er auch wieder. Davon abgesehen kannst ja auch einfach ein Transaction Rollback machen.
Hibernate uaf drop and create tables umstellen wäre nicht wirklich praktikabel da es nicht für jede Testclasse getrennt funktioniert.
Ids zurück setzen sollte auch nicht notwendig sein. Wenn die Tests so geschrieben sind das sie eine bestimmte ID erwarten dann gehört das überdacht.
 
Ok... aber muss ich nicht manche Dinge commiten um sie testen zu können? Wenn ich commite kann ich nachher kein rollback mehr machen...

Oder lassen sich Testfälle immer so designen, dass kein commit notwendig ist?

EDIT: eventuell bin ich auf zufall auf etwas gestoßen, dass mit dem problem zu tun haben könnte... die ids werden nacheinander an user vergeben und nicht einzeln an die entitys. die ids sehen also in der db so aus:
admin 1,2,3
patient 4,5,6
therapist 7,8,9
statt:
admin 1,2,3
patient 1,2,3
therapist 1,2,3

Ok daran kanns eigentlich auch nicht liegen
 
Zuletzt bearbeitet:
Hast wohl GenerateValue = Table in Mapping dann wäre das vollkommen normal.
Es muss nicht direkt ohne Transaction laufen man kann auch einfach die erstellen Objecte einfach normal entfernen.

Ist jetzt aber im Grunde alles ins blaue was ich hier sagen, weil es einfach komplet Situationsabhängig ist.
 
Es wäre einfach schön gewesen für jede Klasse die Testdaten neu einspielen zu können ohne in den Tests selbst großartig darauf zu achten.
Ich denke dass die Probleme irgendwie mit der Hierarchie der Entitys zusammenhängen. Das Problem tritt nämlich nur bei der Usern auf. Bis jetzt bin ich da aber auch nicht weiter gekommen.
 
Debug es halt Schritt für Schritt, und beobachte die Datenbank dabei. Eventuell passiert etwas, mit dem du nicht rechnest.

Sieht mir fast aus, als wäre da unter einer Konstellation mal ein Table leer, von dem du es nicht erwartest.
 
Ich wüsste nicht wie ich eine in memory Datenbank beobachten soll...
 
Keine Ahnung, damit kenn ich mich nicht aus. Aber die muss doch auch einen Treiber haben, der mehrere Connects ermöglicht, solange sie existiert?! Kann jetzt dazu auch nicht mehr sagen :D
 
ja würde schon gehen... ich kann versuchen die db per hand auszulesen... aber am table ansich kanns ja nicht liegen. es werden ja alle felder mit truncate gelöscht :/ also werd ich da auch net viel sehen
 
Ich wiederhole mich nochmal.
Für mich sieht es so aus als ob Hibernate noch gemanaged objecte hat die nachher nimmer da sind da du truncate von hand machst.
Aber ohne den Testcode zu sehen kann ich dazu einfach nichts sagen.
 
Wie meinst du das genau? Die Datenbank wird geleert während Hibernate das nicht mitbekommt und intern noch Objekte hat bzw. weiterverwendet die kein Gegenstück in der DB mehr haben. Das heißt man müsste Hibernate in irgendeiner Form resetten? Ich glaube ich werde die betreffenden Tests einfach nicht hintereinander ausführen. aber interessieren würde es mich trotzdem...
 
Zurück
Oben