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?
Object returnedValue = this.transactionTemplate.execute(s -> doSomethingAndReturnAnObject(parameter));
– Rains