Unit and Integration Tests made easy with Spring
A colleague at the Apache Software Foundation was asking me for info about the Spring Test Framework the other day and I thought I could just share the answer with everyone.
The first thing you would do of course is to read the online documentation here and here in particular if you’re interested to bring transaction management in your tests. While the doc is pretty clear, a small example will probably allows you to get started more rapidly.
Those examples use Spring 2.5 and JUnit 4.4. Note that you need Junit 4.4 to use the annotations based helpers.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/META-INF/myapp-test/foo-test-beans.xml"})
public class MyTest {
@Resource
private MyService myService;
@Test
public void simpleTest() {
// Oh well test something with myService
}
As you can see, two stupid annotations turned out your test class into a Spring-bean! The @Resource (or @Autowired) allows to automatically inject a reference to the service you want to test.
The first annotation, @RunWith, allows for Spring to get called when the test starts and to perform the necessary plumbing for us. The second one, @ContextConfiguration, defines basically what application context your test class will use. You can reduce even more the coding by placing a context file called MyTest-context.xml in the same package as the test. The nice thing about this annotation is that it will cache your application context so if multiple test classes are using the same context, the overall executions will be much faster. If you need advanced features, have a look to the documentation, especially if you want to override the application context of a test that subclasses another one! (have a look to the inheritLocations parameter).
Transaction management can be added easily. The framework will create the transaction for you and rollback everything by default, which means you don’t have to worry about test objects that you would create in the database. Let’s upgrade our simple test so that it can use a transaction.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/META-INF/myapp-test/foo-test-beans.xml"})
@TransactionConfiguration(transactionManager = "transactionManager")
public class MyTest {
@Resource
private MyService myService;
@Test
@Transactional
public void simpleTest() {
// Oh well test something with myService
}
That’s it. Pretty neat, right?
What about exceptions? Checking for expected exceptions is a bit painful and add a whole bunch of code that does nothing. Besides, if the exception is related to the transaction, it’s pretty hard to intercept it in a transactional test because the transaction will commit at the end of it. Hopefully, Spring provides an @ExpectedException that is really handy. Let’s assume that my simpleTest now should throw a SomeException in some Hibernate interceptor. Previously, I would have to turn my test as non transactional and intercept the exception in a try/catch clause. Now I just have to do this
@Test
@Transactional
@ExpectedException(SomeException.class)
public void simpleTest() {
// Oh well test something with myService that will fail as expected
}
There’s other things that you would discover as you use it and it’s definitely worth the effort integrating it in your test suites if you’re using Spring already.
Sunday 15 Feb 2009 | Stéphane | Java
Do you have the source code for this? I’m created a maven project but it seems to ignore the annotations and the test just fails because the annotations are ignored and the context is not loaded when testing.
Make sure you’re using Junit 4.4 and that your compiler is configured with source 1.5 and target 1.5