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.
views:
2069answers:
8Sidestepping 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.
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
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?
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.
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.
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
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.