It's nice to write unit tests. I wanted to write a set of tests for a method that I'd marked with the Spring @Transactional annotation:
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = {MessagingException.class})
@Transformer
public IncomingEmailDTO receiveMessage(Message
IncomingEmailDTO emailDTO = null;
...
The tests for the method were simple. But then I realised I wanted to also test that rollbacks were happening or not as required as specified by the "rollbackFor = {MessagingException.class}" part of the annotation.
A quick aside (because I was asked this by the person who gave me the solution to this, and it's entirely valid). Why did I want to test this? Aren't I just testing that this Spring annotation works? In part, yes, this will be the effect of any tests for this. But there was something else I wanted. Unit tests, over time, build into a massive, executable spec. for your system. It would be very easy for someone in the future to change this small part of the annotation (or remove it altogether) and have a very significant effect on the running of the whole system. Consequently, by adding tests which check that this test is a) transactional, and b) set to rollback for specific exceptions only; I can protect myself against this unfortunate outcome.
But back to the example.
We had the following Spring XML config:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"><context:annotation-config /><tx:annotation-driven transaction-manager="transactionManager" /><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="oracle.jdbc.OracleDriver"/><property name="url" value="jdbc:oracle:thin:@localhost:1521:MYSID"/><property name="username" value="ME"/><property name="password" value="ME"/></bean></beans>
And then in our unit test we had to have the following:
@ContextConfiguration(locations = {"classpath:spring/appContext-incomingEmailReceiverTest.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class IncomingEmailTransformerTest {
@Autowired
private PlatformTransactionManager transactionManager;
@Transactional
@Test
public void test_transform_valid_incoming_email_with_no_attachment_works_and_no_rollback()
throws MessagingException {
IncomingEmailDTO result = emailTransformer.receiveMessage(validInputMessage);
assertFalse(transactionManager.getTransaction(null).isRollbackOnly());
}
@Transactional
@Test
public void test_MessagingException_when_extracting_originator_gets_thrown_and_tx_rolls_back()
throws MessagingException {
stub(mockEmailDataExtractor.extractOriginator(incomingEmailMessage)).toThrow(new MessagingException());
IncomingEmailDTO result = emailTransformer.receiveMessage(validInputMessage);
assertTrue(transactionManager.getTransaction(null).isRollbackOnly());
}
And there we have it.