Setting up a database for unit tests with Spring, Hibernation, and Spring transaction support

I want to test the integration of a dao layer with my service level in a unit test. Therefore, I need to configure some data in my database (hsql). For this setup, I need my own transaction at the beginning of my test test to make sure that all my setup is really loaded into the database before running the test file.

So here is what I want to achieve:

// NotTranactional public void doTest { // transaction begins setup database // commit transaction service.doStuff() // doStuff is annotated @Transactional(propagation=Propagation.REQUIRED) } 

Here is my broken code:

 <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 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> 

My testcase

 @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() { ... } } 

Overcoming this 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) 

I do not know what the correct way to get a transaction around the configuration database.

What I tried:

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

-> Spring doesn't seem to start the transaction in @ Pre-annotated methods. Also, this is not what I really want, because there are a lot of merhods in my test class that require a slightly different setup, so I need a few of these init methods.

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

-> init does not seem to start in a transaction

What I do not want to do:

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

-> start mixing springs with a transaction, and my own seems to get a pain in the ass.

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

-> I want to test the situation as real as possible, so my service should start with a clean session and transactions should not open

So, what is the correct way to install the database for my test system?

+4
source share
2 answers

Adding the following annotations did the trick:

 @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false) @Transactional 

The following is the full configuration:

 @TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false) @Transactional @RunWith(SpringJUnit4ClassRunner.class) @ConnextConfiguration(locations={"/asynchUnit.xml"}) @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) public class ReceiptServiceTest implements ApplicationContextAware { 
+4
source

For spring oriented tests, you can use spring runner for JUnit:

 @RunWith(SpringJUnit4ClassRunner.class) 

documented here:

http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/html/ch10s03.html

And the part that may interest you is that you can comment methods with annotations as follows:

 @Rollback(false) @Test public void testProcessWithoutRollback() { // ... } @BeforeTransaction public void beforeTransaction() { // logic to be executed before a transaction is started } 

and

 @AfterTransaction public void afterTransaction() { // logic to be executed after a transaction has ended } 

you can even use something like

 @NotTransactional @Test public void testProcessWithoutTransaction() { // ... } 

to indicate that you do not want the transaction to apply to it. But keep in mind this document with the following warning:

As with spring 3.0, @NotTransactional is deprecated in favor of moving the transaction-free method for a separate (non-transactional) testing class or the @BeforeTransaction or @AfterTransaction method. As an alternative to annotating an entire class using @Transactional, consider annotating individual methods using @Transactional; doing this allows you to mix transactional and non-transactional methods into the same test class without the need for @NotTransactional.

+3
source

All Articles