How to rollback a database transaction when testing services with Spring in JUnit?

59,538

Solution 1

You need to extend transaction boundaries to the boundaries of your test method. You can do it by annotating your test method (or the whole test class) as @Transactional:

@Test 
@Transactional
public void testInsert(){ 
    long id=myService.addPerson("JUNIT"); 
    assertNotNull(id); 
    if(id<1){ 
        fail(); 
    } 
} 

You can also use this approach to ensure that data was correctly written before rollback:

@Autowired SessionFactory sf;

@Test 
@Transactional
public void testInsert(){ 
    myService.addPerson("JUNIT"); 
    sf.getCurrentSession().flush();
    sf.getCurrentSession().doWork( ... check database state ... ); 
} 

Solution 2

Use Following annotation before class :

@TransactionConfiguration(transactionManager = "txManager",defaultRollback = true)
@Transactional

here txManager is application context's Transaction Manager.

Here txManager is an instance or bean id of Transaction manager from application context.

<!-- Transaction Manager -->
    <bean id="txManager"
          class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <tx:annotation-driven transaction-manager="txManager" />

Add your code inside setUp() method, this will execute in start of the test and the last wrap up code should be put in teatDown() method that will executed at last. or you can also use @Before and @After annotation instead of it.

Solution 3

check out

http://static.springsource.org/spring/docs/2.5.x/reference/testing.html

Section 8.3.4 in particular

Spring has some classes for testing that will wrap each test in a transaction, so the DB is not changed. You can change that functionality if you want too.

Edit -- based on your more infos, you might want to look at

AbstractTransactionalJUnit38SpringContextTests at

http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/context/junit38/AbstractTransactionalJUnit38SpringContextTests.html

Share:
59,538
blow
Author by

blow

Updated on April 10, 2020

Comments

  • blow
    blow about 4 years

    I have no problem testing my DAO and services, but when I test INSERTs or UPDATEs I want to rollback the transaction and not effect my database.

    I'm using @Transactional inside my services to manage transactions. I want to know, is it possible to know if a transaction will be fine, but rollback it to prevent altering database?

    This is my Test:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:/META-INF/spring.cfg.xml")
    @TransactionConfiguration(defaultRollback=true)
    public class MyServiceTest extends AbstractJUnit38SpringContextTests  {
        @Autowired
        private MyService myService;
    
        @BeforeClass
        public static void setUpClass() throws Exception {
        }
    
        @AfterClass
        public static void tearDownClass() throws Exception {
        }
    
        @Test
        public void testInsert(){
            long id = myService.addPerson( "JUNIT" );
            assertNotNull( id );
            if( id < 1 ){
                fail();
            }
        }
    }
    

    The problem is that this test will fail because transaction was rollbacked, but the insert is OK! If I remove @TransactionConfiguration(defaultRollback=true) then the test pass but a new record will be inserted into database.

    @Test
    @Transactional
    @Rollback(true)
    public void testInsert(){
        long id = myService.addPerson( "JUNIT" );
    assertNotNull(id);
    if( id < 1 ){
            fail();
        }
    }
    

    Now can test pass correctly, but rollback is ignored and the record is inserted into the database. I have annotated the method addPerson() inside myService with @Transactional, obviously. Why is the rollback being ignored?

  • blow
    blow over 13 years
    hi, now test pass, but rollback was ignored. I have @Transactional over "testAddPerson" and over "addPerson".
  • blow
    blow over 13 years
    If i remove @Transactional from myService, there is not an active transaction for the test, so i think @Transactional over "testAddPerson" doesn't work...
  • blow
    blow over 13 years
    thank you, this is useful, i found a little problem in my implementation. Now i have another problem, rollback is ignored
  • axtavt
    axtavt over 13 years
    @blow: I just noticed that you have extends AbstractJUnit38SpringContextTests. It's not needed since you have a JUnit 4 test with @RunWith.
  • blow
    blow over 13 years
    you are right. Without AbstractJUnit38SpringContextTests and AbstractTransactionalJUnit38SpringContextTests all works fine. Thank you.
  • Kieren Dixon
    Kieren Dixon over 12 years
    i also had to add @TestExecutionListeners({TransactionalTestExecutionListener.‌​class})