I found that there is nice extension to JUnit called Unitils (http://www.unitils.org). It offers some generic utilities for unit testing, EasyMock mock object management, database testing (either plain JDBC or Hibernate). Also it's integrated with DBUnit and Spring.
General testing utilities
This feature set contains some things that are sometimes quite useful like reflection based equality (which we have more or less implemented ourselves in many projects I believe), ignoring order of collection elements etc. There are also some features that seem somewhat less useful like ignoring default values or asserting fields using the property path (e.g "address.street") instead of getters.
Database testing utilities
The most interesting thing here I think is the ability to test Hibernate mappings using just following statement: HibernateUnitils.assertMappingWithDatabaseConsistent(). If there are some mismatches it will generate the DDL statements that can be used to update your database schema. So to test all your mappings you only need following test case:
@SpringApplicationContext({"testContext.xml"})
public class DatabaseMappingsTest extends UnitilsJUnit4 {
@Test
public void objectsAreMappedCorrectly() {
HibernateUnitils.assertMappingWithDatabaseConsistent();
}
}
and at least following Spring beans XML in your classpath if :
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="annotatedClasses"> <list> <value>hibernate.domain.User</value> <value>hibernate.domain.Permission</value> ... </list> </property> </bean> <bean id="dataSource" class="org.unitils.database.UnitilsDataSourceFactoryBean" />
As I mentioned already Unitils integrates very nicely with Spring so in order to load Spring application context all you need is the @SpringApplicationContext annotation.
Here is a sample for testing Hibernate HQL:
@Transactional(TransactionMode.ROLLBACK)
@SpringApplicationContext({"testContext.xml"})
public class UserDaoImplTest extends UnitilsJUnit4 {
private UserDao userDao;
@SpringBeanByType
private HibernateTemplate hibernateTemplate;
@Before
public void initDao() {
UserDaoImpl dao = new UserDaoImpl();
dao.setHibernateTemplate(hibernateTemplate);
userDao = dao;
}
@Test
public void save() throws Exception {
User user = createNewTestUser();
userDao.save(user);
User retrieved = userDao.getUser(user.getId());
assertEquals(user, retrieved);
}
}
In order to get this sample working following beans need to be added to the testContext.xml
<bean id="hibernateTemplate" class="hibernate.dao.SessionFlushingHibernateTemplate"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref local="sessionFactory" /> </property> </bean>
| Sidenote Save test above is reliable only because of custom HibernateTemplate. Probably everybody already knows this that in order to test Hibernate we need to flush/clear session after every updating method because otherwise Hibernate will not send changes to database and will return exact same instance of entity the next time we try to get it from ongoing session. In AMS there is class called HibernateTemplateProxy which is doing this but it has special registry of deleted entities. Does anyone know why exactly it needs to be there? Especially as the overridden load method is only one way of materializing something from persistence. |
Mock object utilities
Basically Unitils removes the need to manually create mocks and simplifies replaying and verifying. Features like injecting mocks seem a bit fishy to me but maybe I'm just too old fashioned.
Summary
In many aspects Unitils offers similar functionality that is available in Spring 2.5 test packages and probably there are some alternative unit testing libraries also. However if you can't upgrade to Spring 2.5 or want to simplify testing Hibernate/DB access code I think Unitils is quite handy.