views:

2069

answers:

8

I want to test my Dao Class using the SpringContextTests.
In my method class I extended the AbstractTransactionalJUnit4SpringContextTests in order for my test class to integrate with JUnit4. I have also set up the configurations and made the initialization and database clean up in the @Before and tearDown in the @After. My test class works perfectly.

My problem was, when I run my test class and the database is filled with data, the original data was not rolled back and my database is cleared. In the @Before method, I clear the database and populate data, thinking that I will be able to rollback it but its not.

Can anyone site an example that works and rollbacks information in the database.

ADDONS:
Every database manipulation in my test methods are rolled back. But the execution of super.deleteFromTables("person") in the @Before method did not rollback all the previous data from the database.

Spring rollbacks all the CRUD operations but the database clean up before the transaction do not rollback.

A: 

Sidestepping your question, I suggest that you use a seperate database instance to run your tests against. That way, you can safely wipe it clean and have your tests initialize it as required.

As far as I know the Spring support classes for database testing only rollback what happens in the tests, not what happens in setup and teardown of tests.

Confusion
A: 

Agree with Confusion-- you should be running your tests against their own database schema.

With this, you can set your hibernate properties to 'create-drop':

With create-drop, the database schema will be dropped when the SessionFactory is closed explicitly.

See: Optional Hibernate Config properites

Example snippet:

<bean id="sessionBean" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
         ...etc
Cuga
A: 

While I'd agree with the guy's suggesting a deciated db for testing, there isn't any reason why using a populated db shouldn't work, both @Before and @After methods are executed within the transactional context, therefore there changes should be rolledback.

Possibles:

  • The data setup is doing something that isn't transactional (ie DDL statements)
  • Something in your test is actually committing the transaction

Can you post the @Before method, I'm just wondering if you are just clearing the tables or actually dropping and recreating them?

Gareth Davis
I use the testing mechanism provided by Spring. In my `@Before` method, `super.deleteFromTables("members");``super.executeSqlScript("classpath:somescript.sql",true);`
Roy Marco Aruta
+1  A: 

Possible causes:

  • you're using a database/database engine which does not have proper transactions;
  • you're using multiple transaction managers and/or data sources and the proper one is not picked up;
  • you're doing your own, separate, transactions in the test class


As for an example, here's one ( top of my head, not compiled )

public class DBTest extends AbstractTransactionalJUnit4SpringContextTests {

    @Autowired
    private SomeDAO _aBeanDefinedInMyContextFile;

    @Test
    public void insert_works() {
        assert _aBeanDefinedInMyContextFile.findAll() == 0;
        _aBeanDefinedInMyContextFile.save(new Bean());
        assert _aBeanDefinedInMyContextFile.findAll() == 1;
    }


}

Key points:

  • the SomeDAO is an interface which corresponds to a bean declared in my context;
  • the bean does not have any transactional settings ( advice/programmatic), it relies on the caller being transactional - either the service in production, or the test in our situation;
  • the test does not include any transactional management code, as it's all done in the framework.
Robert Munteanu
My DAO Implementation extends the SimpleJdbcDaoSupport provided by Spring for easier access to the database. Does my DAO have a transactional management code? If my DAO has transactional management, how do I test it?
Roy Marco Aruta
By default it should not have. Enable debug logging for Spring to see more.
Robert Munteanu
A: 

As far as I can tell, by looking at the Javadocs and source code for AbstractJUnit4SpringContextTests and TransactionalTestExecutionListener you need to annotate your test methods you want transactionalised with @Transactional.

There are also @BeforeTransaction and @AfterTransaction annotations where you can better control what runs in a transaction.

I suggest you create methods annotated with all these annotations, including @Before and then run the test with breakpoints at these methods. That way you can look at the stack and work out whether spring has started a transaction for you or not. If you see something like "TransactionInterceptor" in the stack then, or anything else with "Transaction" in the name, then chances are you're in a transaction.

Michael Wiles
A: 

I'm not sure what is wrong with your class. Here is an extract of a class that does what you want with dbunit and spring 2.5:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
  "testContext.xml"})
@TransactionConfiguration
@Transactional
public class SampleDAOTest {

    @Autowired
    private DataSource dataSource;
    @Autowired
    private SampleDAO sampleDAO;

    @Before 
    public void onSetUpInTransaction() throws Exception {
     //Populate Test data
     IDatabaseConnection dbUnitCon = new DatabaseConnection(DataSourceUtils.getConnection(dataSource), "DATASOURCE");
            //read in from a dbunit excel file of test data
     IDataSet dataSet = new XlsDataSet(new File("src/test/resources/TestData.xls"));
     DatabaseOperation.INSERT.execute(dbUnitCon, dataSet);
    }


    @Test
    public void testGetIntermediaryOrganisation() {

     // Test getting a user
     User object = sampleDAO.getUser(99L);
     assertTrue(object.getValue);


    }
}

One of the benfits of this method is that you don't need to extend any classes. So you can still have your own hierarchy for tests.

If you really want to stick to your current method instead of using the @before annotation I thinnk you need to overide the below method and put your setup code in there.

@Override
public void onSetUpInTransaction() throws Exception {...}

Hope this helps

Pablojim
+1  A: 

Thank you to all those who answered my question. I learned a lot from those answers but it didn't solve my problem.
I knew my test data does a transaction management and it does its job properly.
The mistake is on my part.

I forgot the lesson about database commands that when you execute a DDL statement after a DML statement, it will automatically commit the transaction. I executed a DDL after a DML by deleting all record and then ALTER the AUTO_INCREMENT of the table where in it will cause an auto-commit and delete all records of the table permanently.

FIXING THAT SCENARIO SOLVED MY PROBLEM.

Roy Marco Aruta
A: 

You're doing super.deleteFromTables in your @Before method which is within the tx. So if the tx is rolled back doesn't the deletions get rolled back also?