Hibernate + Spring - Testing the DAO Layer with an In Memory Database

For some time I have been working on developing a Java web app using Spring MVC & Hibernate, and as many will have discovered, this throws up lots of questions with unit testing. To increase my coverage (and general test confidence) I decided to implement some tests around the DAO layer. Up untill now, the DB access had been mocked out, and whilst some purists will argue that these are strictly "Integration tests" rather than unit tests, I decided to go ahead with the.

As I didn't want to be messing around with going near my dev DB, or worrying about dropping my dev data everytime I run a test I decided to test the DAO layer using in memory DB (HSQL), so here's how its done:

First, I added the dependency to my POM so the HSQL JARs were downloaded:

        
            hsqldb
            hsqldb
            1.8.0.10
            jar
            test
        


Next, in my Persistence.xml I added a new Persistence Unit, that would point my tests to my in-mem DB

    
        
            
            
            
            
            
            
            
            
        
    


Important points to note here are that the database name can be anything you like (well, as long as you have the url jdbc:hsqldb:mem: ..). Also, the username must be "sa" and the password "". There is no manual install needed, or explicit DBs created, as long as you have the JAR in your project everything will be handled for you.

Next, I copied my regular applicationContext.xml to src/test/resources, as I needed to configure this to point to my new Persistence Unit defined (and new data source)



    
    
    
        
    
   
    
        
    
   
    
   
    
   
    
        
    



As you can see, I have updated my Entity Manager Factory Bean to be using my newly created Test Persistence unit - this means any time Spring is fired up using the context definition, it will be pointing to my in-mem DB - Now I have configured everything, I am ready to start testing.

I started by writing an abstract test class - I used this to define my common @Before method (I needed to setup a common "test" user to allow db updates to complete), but the important thing here is the annotations on the class

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/test-applicationContext.xml"})
@Transactional
public abstract class AbstractServiceTest {


These annotations tell the tests that they are running with the Spring junit runner, and to use my newly created test-applicationContext.xml. The @Transactional is neccesary so we know to roll back all data persisted during the tests at the end of each test (we dont want test data leaking in to other tests and potentially affecting the outcome).

Now its just a case of writing the tests, here is an example of a test:

public class AccountServiceDbTest extends AbstractServiceTest{
   
    @Autowired
    AccountService service;

    @Test
    public void testFindAllAccounts() {
        List accs = service.findAllAccounts();
        assertEquals("Check DB is empty first", 0, accs.size());
        Account a = new Account();
        a.setUserName("robb");
        a.setPassword("password");
        service.storeAccount(a);
        accs = service.findAllAccounts();
        assertEquals("check Account has been created", 1, accs.size());
    }

}


The test checks that nothing is in the DB, then persists the new account to the table (all in-memory) and then checks the size to make sure it has been created correctly. At the end of the test the transaction is rolled back, so this will always work!

Another interesting point, is as I have defined the Spring context in the Abstract class, my tests are all now Sprin-managed, which means I can simply use the @Autowired annotation to autowire other Spring classes such as Service classes (these have my DAOs etc in).

1 comment: