Autowired JUnit Tests with Spring 2.5
posted on 05 Jan 2009 09:15 by sonicneo in JavaSpring 2.5 ships with great support for integration testing through the classes in the org.springframework.test package. These classes allow you to dependency inject your test cases off of your existing Spring configuration, either by using your production Spring configuration file or one you've defined especially for the test case. This post explains how to annotate your JUnit 4 test cases to be autowired, but there's a lot more too the new spring-test.jar and it works with JUnit 3.8 also.
As setup for all the examples, let's assume there is a simple service that can return a String to you. Imaginative, huh?
public class MyService {
private final String name;
public MyService(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
This service is defined in a Spring XML configuration file as:
To
have Spring dependency inject our test case, we're going to have to
annotate it with a little information. We'll use the standard JUnit @RunWith annotation to tell JUnit to use the Spring TestRunner, the Spring @ContextConfiguration annotation to indicate the TestCase needs injection, and the Spring @Autowired annotation to inject the MyService field by type.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public final class SimpleAutowireTest {
@Autowired
private MyService service;
@Test
public void testServiceName() {
assertEquals("simple service", service.getName());
}
}
There
are a variety of ways to have Spring run your test. Besides
SpringJUnit4ClassRunner.class there are also abstract TestCase objects
you can inherit from, including some that will expose the
ApplicationContext to you. By default, the spring configuration file
for the test case will be [TestName]-context.xml. You can easily
override this to point the test at your production configuration file.
Also, the @Autowired annotation autowires the field by type. In the
case of conflicts, you can specify the bean name using the @Qualifier
annotation. Consider the following Spring configuration where there are
two beans of type MyService in a file named applicationContext.xml:
The
JUnit test now requires the @Qualifier field annotation and a
configuration file location as an @ContextConfiguration parameter:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"applicationContext.xml"})
public final class AdvancedAutowireTest {
@Autowired
@Qualifier("deliveryService")
private MyService service;
@Test
public void testServiceName() {
assertEquals("delivery service", service.getName());
}
}
To
note: if you point the test to the production Spring configuration,
then you're running integration tests and not unit tests. Integration
tests are valuable and necessary, but are not always a wholesale
replacement of unit tests. There's still value in isolation testing
each of the components with mock dependencies. What I like about using
Spring for the integration tests is that it validates the Spring
mapping at test time. On my projects, we occasionally have the unit
tests all passing but the Spring configuration contains a circular
dependency, and we don't find out until the build hits QA. A Spring
integration test pushes up error time to the test phase, when a
developer can take quick action and long before QA is held up.As is standard with JUnit, each test method runs within its own instance of the TestCase. However, the Spring TestRunner will only create one ApplicationContext for all the instances of the test method. This means that if you're test method destroys the state of a singleton bean, then downstream tests methods using that bean will fail. The way to work around this is to annotate your method with the @DirtiesContext annotation. This tells the Spring TestRunner to reload the context between test methods. For instance, these tests will pass when annotated, but fail when not:
@Autowired
private MyConfigurableService service;
@Test
@DirtiesContext
public void testServiceName1() {
assertEquals("configurable service", service.getName());
service.setName("Updating Name");
}
@Test
@DirtiesContext
public void testServiceName2() {
assertEquals("configurable service", service.getName());
service.setName("Updating Name");
}
There's
a lot more in spring-test, but this is a good starting point. The
transaction annotations look especially promising, including the
ability to open and rollback transactions per method. The only
reference I know for spring-test is the Javadoc at http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/test/package-summary.html. Those looking for a Spring starter lesson might consult the DZone RefCard.
The
dependencies for spring-test are: junit, spring-context, spring-core,
spring-beans, spring-tx, and commons-logging. A sample Gradle script
for your build is at href="http://svn.assembla.com/svn/SampleCode/spring-test/build.gradle.
All the code for this post (including Gradle build script and IDEA project) is posted at http://svn.assembla.com/svn/SampleCode/spring-test/ and can be downloaded through subversion with the following command:
svn co http://svn.assembla.com/svn/SampleCode/spring-test
source : http://hamletdarcy.blogspot.com/search/label/Spring
edit @ 5 Jan 2009 09:17:01 by sonicneo