I’m very keen on the improvements in Spring 3 that eventually let you move away from XML into plain Java configuration with proper support from IDE and compiler. It doesn’t change the fact that Spring is a huge suite and it sometimes finding the thing you need can take a while.
XML-free unit tests around Hibernate are one such thing. I knew it was possible, but it took me more than 5 minutes to find all the pieces, so here I am writing it down.
I am going to initialize all my beans in a @Configuration class like this:
@Configuration
@EnableTransactionManagement
public class TestRepositoryConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
.setName("Nuts").build();
}
@Bean
public LocalSessionFactoryBean sessionFactoryBean() {
LocalSessionFactoryBean result = new LocalSessionFactoryBean();
result.setDataSource(dataSource());
result.setPackagesToScan(new String[] { "pl.squirrel.testnoxml.entity" });
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
result.setHibernateProperties(properties);
return result;
}
@Bean
public SessionFactory sessionFactory() {
return sessionFactoryBean().getObject();
}
@Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager man = new HibernateTransactionManager();
man.setSessionFactory(sessionFactory());
return man;
}
@Bean
public OrderRepository orderRepo() {
return new OrderRepository();
}
}
… and my test can look like this:
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(defaultRollback = true)
@ContextConfiguration(classes = { TestRepositoryConfig.class })
@Transactional
public class OrderRepositoryTest {
@Autowired
private OrderRepository repo;
@Autowired
private SessionFactory sessionFactory;
@Test
public void testPersistOrderWithItems() {
Session s = sessionFactory.getCurrentSession();
Product chestnut = new Product("Chestnut", "2.50");
s.save(chestnut);
Product hazelnut = new Product("Hazelnut", "5.59");
s.save(hazelnut);
Order order = new Order();
order.addLine(chestnut, 20);
order.addLine(hazelnut, 150);
repo.saveOrder(order);
s.flush();
Order persistent = (Order) s.createCriteria(Order.class).uniqueResult();
Assert.assertNotSame(0, persistent.getId());
Assert.assertEquals(new OrderLine(chestnut, 20), persistent
.getOrderLines().get(0));
Assert.assertEquals(new OrderLine(hazelnut, 150), persistent
.getOrderLines().get(1));
}
}
There are a few details worth noting here, though:
- I marked the test
@Transactional, so that I can accessSessiondirectly. In this scenario,@EnableTransactionManagementon@Configurationseems to have no effect as the test is wrapped in transaction anyway. - If the test is not marked as
@Transactional(sensible when it only uses@Transactionalcomponents), the transaction seems to always be committed regardless of@TransactionConfigurationsettings. - If the test is marked as
@Transactional,@TransactionConfigurationseems to be applied by default. Even if it’s omitted the transaction will be rolled back at the end of the test, and if you want it committed you need@TransactionConfiguration(defaultRollback=false). - This probably goes without saying, but the
@Configurationfor tests is probably different from production. Here it uses embedded H2 database, for real application I would use a test database on the same engine as production.
That’s it, just those two Java classes. No XML or twisted depedencies. Take a look at my github repository for complete code.