Thursday, 18 March 2010

Unit Testing with multiple daos - AbstractTransactionalDataSourceSpringContextTests

I had a scenario where I wanted to unit test my dao methods , however some of the daos depended on data being provided from other daos to run successfully.

In order to achieve the same I required to be able to configure multiple data sources in my spring config and inject the data sources as and when required.

I used AbstractTransactionalDataSourceSpringContextTests for my Junit test cases.

And my spring config xml looked like :



<!-- Database 1-->
<bean id="testDataSource1" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">

</bean>

<!-- Database 2 -->
<bean id="testDataSource2" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">

</bean>

<bean id="dao1" class="Dao1Impl">
<property name="dataSource" ref="testDataSource1">
</property></bean>

<bean id="dao2" class="Dao2Impl">
<property name="dataSource" ref="testDataSource2">
</property></bean>

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="testDataSource1">
</property></bean>


When I re ran the unit test . I came across the following error:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'DaoTest': Unsatisfied dependency expressed through bean property 'dataSource': No unique bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: [testDataSource1, testDataSource2]

I had to change my unit test so that it looked like below:


import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;


public abstract class BaseTest extends AbstractTransactionalDataSourceSpringContextTests {

@Autowired
Dao1 dao1;

@Autowired
Dao2 dao2;

@Autowired
DataSource testDataSource1;

@Autowired
DataSource testDataSource2;

/* (non-Javadoc)
* @see org.springframework.test.AbstractSingleSpringContextTests#getConfigPath()
*/
@Override
protected String getConfigPath() {
return "spring-test-config.xml";
}

@Override
@Autowired
public void setDataSource(@Qualifier("testDataSource1") final DataSource dataSource){
super.setDataSource(dataSource);
}

/**
* @return the dao1
*/
public Dao1 getDao1() {
return dao1;
}

/**
* @param dao1 the dao1 to set
*/
public void setDao1(Dao1 dao1) {
this.dao1 = dao1;
}

/**
* @return the dao2
*/
public Dao2 getDao2() {
return dao2;
}

/**
* @param dao2 the dao2 to set
*/
public void setDao2(Dao2 dao2) {
this.dao2 = dao2;
}

/**
* @return the testDataSource1
*/
public DataSource getTestDataSource1() {
return testDataSource1;
}

/**
* @param testDataSource1 the testDataSource1 to set
*/
public void setTestDataSource1(@Qualifier("testDataSource1") DataSource testDataSource1) {
this.testDataSource1 = testDataSource1;
}

/**
* @return the testDataSource2
*/
public DataSource getTestDataSource2() {
return testDataSource2;
}

/**
* @param testDataSource2 the testDataSource2 to set
*/
public void setTestDataSource2(@Qualifier("testDataSource2") DataSource testDataSource2) {
this.testDataSource2 = testDataSource2;
}



}

the interesting thing above is the @Qualifier annotation which allows us to reference which data sources to inject while initializing the beans.

Now while writing my test cases I can simply switch between the datasources like :

setDataSource(getTestDataSource2());
// Perform transaction related to data source 2
setDataSource(getTestDataSource1());
// Set the data source back to the main data source


I know Unit test cases must be independent , and should be testing on real time data , however if in case a need arises to be able to test the same without having to worry about re deploying and re starting application servers , this method is useful.