February 22, 2010

Unit testing with Hibernate and Spring

« Native Maven Plugin - Build native C/C++ projects with Maven | Main | Trend between Spring and EJB job requirements »

Many applications use hibernate as OR-Mapper, i want show an easy and fast way how to unit test your DAOs and domain model.

setup

we use a common use case: article ordering.

following image shows the layout of the maven project

 maven project layout

domain model

we use hibernate annotations to define our model  following code extract is taken from ArticleDTO

...
@Entity
@Table(name = "article")
public class ArticleDTO {

    private long id;

    private ArticleGroupEnum group;

    private String name;

    private double price;

    private long contract;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="article_id")
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Enumerated(EnumType.ORDINAL)
    @Column(name="articlegroup")
    public ArticleGroupEnum getGroup() {
        return group;
    }
... 

 spring config

as seen above we use several spring context files to define our application context.

  • multi-db-datasource-ctx.xml

productive datasource definition, in this example we use a mysql database and a default transaction manager
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/developersblog"/>
<property name="username" value="developer"/>
<property name="password" value="blogdevelopers"/>
<property name="initialSize" value="2" />
<property name="maxActive" value="8" />
<property name="maxIdle" value="2" />
<property name="minIdle" value="2" />
<property name="maxWait" value="-1" />
<property name="testWhileIdle" value="true"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="org.developers.blog.db.dto"/>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=false
</value>
</property>
</bean>

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
  • multi-db-service-ctx.xml

here we define our service class, helpers,and so on ...
 <bean id="dbService" class="org.developers.blog.db.DbService">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
  •  multi-db-ctx.xml

just aggregating (import) above two context files

let's turn to our test environment

  • hsqldb-ctx.xml

test datasource definition (according to multi-db-datasource-ctx.xml)
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:developersBlog"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="org.developers.blog.db.dto"/>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.format_sql=true
hibernate.hbm2ddl.auto=create
hibernate.show_sql=true
use_sql_comments=true
</value>
</property>
</bean>

as you can see, we use hsqldb as in memory database.. 

specified property hibernate.hbm2ddl.auto=create tells hibernate to create schema definition for (in memory) database setup

  • import.sql

 as stated in hibernate documentation, if hibernate.hbm2ddl.auto is set, hibernate searches after schema creation for an import.sql file and executes this sql script.
this is an easy to use way, to insert default data via sql into specified in memory database
INSERT INTO article (article_id, articlegroup, description, price, contract_id) 
                                  VALUES ('1', '1', 'erster test article', '9.99', '2'); 

test class

now it's pretty easy to use this test environment. In our test class we specify above mentioned hsqldb-ctx.xml as application context as well as multi-db-service-ctx.xml and let spring autowire the beans.


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:hsql-db-ctx.xml",
"classpath:multi-db-service-ctx.xml"})
public class DbServiceIntegrationTest {


@Autowired
private SessionFactory sessionFactory;

@Autowired
private IDbService sut;

now we can answer the question, why we have an own application context file for our service bean and a seperated context file for datasource issues. Since both (production:mysql, testing:hsqldb) datasource beans have the same name, they get replaced according to used contexts.

test methods are pretty easy

 /**
* simple get infos from db.
* where does this data come from ? see import.sql
*/
@Test
public void testApp() {

List<ArticleDTO> result = sut.getByContract(2);
assertEquals(1, result.size());

ArticleDTO article = result.get(0);
assertEquals(1, article.getId());
assertEquals(9.99d, article.getPrice(), 0.00d);
assertEquals(ArticleGroupEnum.FOOD, article.getGroup());

}

no need to insert/create dummy data and remove them later on. I usually add test data (statements in import.sql) for each test case.

Technorati Tags:

Posted by peter.grund at 8:42 PM in Spring

 

[Trackback URL for this entry]

Comment: Adam Knight at Mo, 15 Mrz 1:25 AM

Very useful - thanks for posting!

Comment: p krishna at Sa, 8 Mai 9:38 PM

I am newbie to Spring and Hibernate. Can you please post the src code. It'll be very helpful for me.

Your comment:

(not displayed)
 
 
 

Live Comment Preview:

 
« February »
SunMonTueWedThuFriSat
 123456
78910111213
14151617181920
21222324252627
28