Spring integration test with manual transaction management

3.5k views Asked by At

I've read many posts and threads about integration testing with Spring but nothing is either satisfying or helpful.

We're using Spring 3.2.3 with Hibernate, Spring Data and an Oracle database. For testing we also use DbUnit and Spring-test-dbunit. In production code, the transaction is started by a controller, the service itself does not know anything about a transaction.

So, here is my test:

@ContextConfiguration // ...
@ActiveProfiles // ...
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({
    DependencyInjectionTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class,
    TransactionalTestExecutionListener.class,
    ModifiedDbUnitTestExecutionListener.class })
@DbUnitConfiguration(databaseConnection = "oracleConnection")
@DatabaseSetup("/database/snapshot/snapshot.xml")
public class IntegrationTest extends AbstractTransactionalJUnit4SpringContextTests
{
    @Test
    public void sampleTest()
    {
        // transaction is already started

        this.assertThatNewsContains(0);

        News news1 = new News();
        news1.setTitle("Test News 1");
        News savedNews1 = this.newsService.save(news1);
        Assert.assertTrue(savedNews1.getId() > 0);

        News news2 = new News();
        news2.setTitle("Test News 2");
        News savedNews2 = this.newsService.save(news2);
        Assert.assertTrue(savedNews2.getId() > 0);

        News news3 = new News();
        news3.setTitle("Test News 3");
        News savedNews3 = this.newsService.save(news3);
        Assert.assertTrue(savedNews3.getId() > 0);

        // transaction commit should occur HERE
        // @todo: HOW ?!

        this.assertThatNewsContains(3);
    }

    private void assertThatNewsContains(int newsSize)
    {
        List<News> allNews = this.newsService.getNews();
        Assert.assertEquals(newsSize, allNews.size());
    }

}

What I found out is that, if I annotate the NewsService with @Transactional(propagation=Propagation.REQUIRES_NEW) the test works fine, however it is not the same as in production mode. @Transactional(propagation=Propagation.REQUIRED) is not sufficient as the DbUnit-Spring-test opens a transaction by itself and the latter assert fails as the transaction is not committed yet. How can I achieve that a transaction is committed BEFORE the last assert is executed?

2

There are 2 answers

1
actc On BEST ANSWER

I finally managed to execute some code in a separate transaction.

You need

@Autowired
private PlatformTransactionManager          platformTransactionManager;

private TransactionTemplate             transactionTemplate;

in your test class. Then you can do the following:

this.transactionTemplate = new TransactionTemplate(this.platformTransactionManager);

// note that parameters passed to the transaction must be final!
final Object parameter = something;

Object returnedValue = this.transactionTemplate.execute(new TransactionCallback<Object>()
    {
        @Override
        public Object doInTransaction(TransactionStatus status)
        {
            return doSomethingAndReturnAnObject(parameter);
        }
    }
);
4
Anudeep Gade On

As mentioned in the documentation

http://springtestdbunit.github.io/spring-test-dbunit/

If you have configured DBUnit tests to run using the are DbUnitTestExecutionListener and are also using the TransactionalTestExecutionListener you may experience problems with transactions not being started before your data is setup, or being rolled back before expected results can be verified. In order to support @Transactional tests with DBUnit you should use the TransactionDbUnitTestExecutionListener class.

And to initiate transaction you have to annotate your test method or class with @Transactional annotation like this.

@Test
@Transactional
    public void sampleTest()
    {