Le programme d'installation de la base de données pour les tests Unitaires avec Spring, Hibernate et Spring Soutien à la Transaction

Je veux tester l'intégration de dao-couche avec mon service-couche dans un test unitaire. J'ai donc besoin de certaines données dans ma base de données (hsql). Pour cette configuration, j'ai besoin d'un propre transaction au début de mon cas de test pour s'assurer que toute mon installation est vraiment imprégné de la base de données avant de commencer mon cas de test.

Alors, voici ce que je veux réaliser:

//NotTranactional
public void doTest {
  //transaction begins
  setup database
  //commit transaction

  service.doStuff() //doStuff is annotated @Transactional(propagation=Propagation.REQUIRED)
}

Voici ma pas de code de travail:

<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="org.hsqldb.jdbcDriver"
p:url="jdbc:hsqldb:mem:posmail" p:username="sa"
p:password="" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>        
<value>de.diandan.asynch.modell.receipt.Position</value>
<value>de.diandan.asynch.modell.receipt.Receipt</value>
</list>
</property>    
<property name="hibernateProperties">
<props>
<prop key="hibernate.connection.useUnicode">true</prop>
<prop key="hibernate.connection.characterEncoding">UTF-8</prop>
<prop key="hibernate.connection.charSet">UTF-8</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<!-- Enable Hibernate's automatic session context management 
<prop key="current_session_context_class">thread</prop> -->
<!-- Disable the second-level cache  -->
<prop key="cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager" 
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

Mon Cas De Test

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/asynchUnit.xml"})
@DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
public class ReceiptServiceTest implements ApplicationContextAware {
@Autowired(required=true)
private UserHome userHome;
private ApplicationContext context;
@Before
@Transactional(propagation=Propagation.REQUIRED)
public void init() throws Exception {
User user = InitialCreator.createUser();
userHome.persist(user);
}
@Test
public void testDoSomething() {
...
}
}

Menant à cette exception:

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687)
at de.diandan.asynch.modell.GenericHome.getSession(GenericHome.java:40)
at de.diandan.asynch.modell.GenericHome.persist(GenericHome.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)
at $Proxy28.persist(Unknown Source)
at de.diandan.asynch.service.ReceiptServiceTest.init(ReceiptServiceTest.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
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)

Je ne sais pas quel est le bon moyen de faire la transaction autour de l'installation de la base de données.

Ce que j'ai essayé:

@Before
@Transactional(propagation=Propagation.REQUIRED)
public void setup() {
setup database
}

-> le Printemps ne semble pas lancer la transaction dans le @Avant-annoté méthodes. Au-delà, ce n'est pas ce que je veux vraiment, car il y a beaucoup merhods dans mon testclass qui a besoin d'un peu différent de l'installation, donc j'ai besoin de plusieurs de init-méthodes.

@Transactional(propagation=Propagation.REQUIRED)
public void setup() {
setup database
}
public void doTest {
init();
service.doStuff() //doStuff is annotated @Transactional(propagation=Propagation.REQUIRED)
}

--> init semble pas à se lancer dans l'opération

Ce que je ne veux pas faire:

public void doTest {
//doing my own transaction-handling
setup database
//doing my own transaction-handling
service.doStuff() //doStuff is annotated @Transactional(propagation=Propagation.REQUIRED)
}

--> commencer à mélanger les ressorts de la transaction et de traitement de ma propre semble être une douleur dans le cul.

@Transactional(propagation=Propagation.REQUIRED)
public void doTest {
setup database
service.doStuff()
}

--> je veux tester aussi réel que possible de la situation, de sorte que mon service doit démarrer avec une nouvelle session et aucune transaction ouverte

Alors quel est le bon chemin pour l'installation de la base de données pour mon cas de test?

Veuillez également vous assurer que votre base de données prend en charge les transactions. Par exemple, MySQL ne prendra pas en charge les transactions jusqu'à ce que InnoDB dialecte est définie pour les tables.
Oui, je suis en utilisant innodb

OriginalL'auteur mibutec | 2012-06-10