views:

277

answers:

2

I am testing my Hibernate DAOs with Spring and JUnit.

I would like each test method to start with a pre-populated DB, i.e. the Java objects have been saved in the DB, in a Hibernate transaction that has already been committed. How can I do this?

With @After and @Before, methods execute in the same Hibernate transaction as the methods decorated with @Test and @Transactional (first level cache may not be flushed by the time the real test method starts). @BeforeTransaction and @AfterTransaction apparently cannot work with Hibernate because they don't create transactions even if the method is annotated with @Transactional in addition to @Before/AfterTransaction.

Any suggestion?

+2  A: 

One way could be to externalize your intialization logic to an external service with transactional methods which are executed from your @BeforeTransaction and @AfterTransaction annotated methods in the test class.

Another benefit of this approach is the reusability of initialization code across tests.

You could for example use the SpringJunit4ClassRunner like described here like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"testContext.xml","services.xml"})
public class MyServiceTest {

    @Autowired    
    private TestDataService testDataService;

    @Before
    public void setUp(){
        testDataService.addTestData();
    }

    @Test
    public void testSomething() throws Exception {
         // ...
    }
}


public interface TestDataService {

    void addTestData();

}

public class TestDataServiceImpl implements TestDataService {

    @Transactional
    public void addTestData(){
        // TODO
    }

}

That's something we do in our Spring based projects.

Make sure the transactional configuration is correct. If you want to avoid the class/interface separation, then set proxy-target-class to true in the element.

Timo Westkämper
Thanks Timo, I like your answer, but I could not inject such an initialization service into my test object (simple DAOs and beans that do no depend on a Hibernate session factory work great though). I have the impression that the application context startup is not standard. Any idea of what might be going on?
Francois
With JUnit 4.5 and Spring 3, I cannot inject in my test classes services annotated with @Transactional. Services not annotated with @Transactional can be injected just fine though.
Francois
@Francois, do they simply not get injected or do they throw any Exception? Also do yo use Interface/Class separation? Autowired matches by type, not by name. I need some more information to help you.
Timo Westkämper
Thanks Timo.Do they simply not get injected or do they throw any Exception? They throw an exception when injected. If I list the beans from the application context, they are here, but casting them fails. If I remove all @Transaction annotations, it works.Also do yo use Interface/Class separation? The TestSetup service family is entirely separate from the test classes.The TestService family follows a simple singletons / autowire by type paradigm.
Francois
Ok, my guess is that something goes wrong with Spring AOP proxying, I posted an example that should work.
Timo Westkämper
I think so too. Thanks for your help though. Your solution should work.
Francois
+1  A: 

dbUnit is a good framework for the job.

In short, it should be started from the setUp() method and it deletes all contents from specified tables, then fill them up with content from a XML file.

Else you can try to execute the setUp() method in a new transaction like this:

@Before
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void setUp() {
    // initial logic .. 
}
Espen