Question

I am writing an application using Spring. I want to write my JDBC code to be transactional, which I can achieve by using AOP:

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true" rollback-for="MyCustomException"/>
        <tx:method name="*" rollback-for="MyCustomException" />
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="pc" expression="execution(* com.me.jdbc.*.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc" />
</aop:config>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
    <constructor-arg ref="simpleDataSource" />
</bean>

<bean id="simpleDataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
    <property name="driverClass" value="org.h2.Driver" />
    <property name="username" value="sa" />
    <property name="password" value="" />
    <property name="url" value="jdbc:h2:mem:aas;MODE=Oracle;DB_CLOSE_DELAY=-1;MVCC=true" />
</bean>

So all my methods in com.me.jdbc should be transactional and rollback if a MyCustomException occurs. This works so far.

I now want to write a unit test, but I want my unit test to be transactional, such that, once the test is complete the entire transaction will roll back and leave the database the way it was at the start.

I am able to achieve this by declaring:

@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class })
@Transactional

on my Test class. The problem however is that I lose the transactions on the application code, i.e. after each test the transactions are rolled back, but if an exception occurs in the application code the insert is not rolled back and my unit test fails because I expect the table to be empty, but it isn't.

How would I go about achieving these two levels of transaction? Is it possible? I can work around the problem by writing a setup method for my unit tests that clears out all of the tables before each test. I am fine with this, but I thought it would be good to be able to use the rollback method of achieving the same thing.

Was it helpful?

Solution

As posted in the comments section, the issue is that by making the test method encapsulate a transaction, the rollback will only occur if the test throws the exception. This makes it impossible to verify the behaviour under test before the exception is thrown. I have gone with the approach of just emptying all the tables in the setup method (before each test).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top