I like injecting dependencies. It makes my code a lot cleaner and easier to deal with.
So, when I find a service locator lookup method such as a Spring getBean("foo), my nose turns up a little.
On a current project, I see these getBean() calls in various JUnit tests. The Spring Beans themselves often have a public field called springBeanName, which the test goes too, to look up the right bean.
Initial Code
This meant that the code looked like this:
A Base Test Case class has within it:
Code to configure logging
Code to setup Spring:
String[] paths = {"classpath:applicationContext*.xml"};
ctx = new XmlWebApplicationContext();
ctx.setConfigLocations(paths);
ctx.setServletContext(new MockServletContext(""));
ctx.refresh();
Some mock objects which various test classes can grab
Helper methods to get Spring managed beans. E.g. a DAO:
protected UserDAO getUserDao() {
return (UserDAO) getBean(UserDAO.springBeanName);
}
Then a test itself will extend the base class, and either grab a bean from a helper method, or do a getBean() themselves.
Injecting into JUnit classes
I want to be able to put my set/get injection methods on my test classes, and have Spring do the rest.
Luckily, Colin and Dmitriy pointed me to org.springframework.test.AbstractDependencyInjectionSpringContextTests, which likes to hang out in the Spring Mocks package (not core Spring).
It simply allows you to bootstrap your test class, and have Spring wire you up nicely. I changed my BaseTestCase to now implement this abstract class, and overrite the method which returns the config locations. Something like:
protected String[] getConfigLocations() {
return new String[] { "classpath:applicationContext*.xml" };
}
Now, in my test cases I setup a get/set combo for the types that I wish to inject. For example:
public class AddSiteTest extends BaseTestCase {
public void testAddingValidSite() throws Exception {
TestCase.assertEquals("AddSiteTest action did not return SUCCESS", Action.SUCCESS, getAction().execute());
}
// ------------------------------------------------------------------------
// Spring Dependencies: BaseTestCase will autowire by type
// ------------------------------------------------------------------------
private AddSite action;
public AddSite getAction() {
return action;
}
public void setAction(AddSite action) {
this.action = action;
}
}
What I really want: No auto-wire, let me wire it
This has got me pretty much what I wanted. My test classes not have dependencies injected. However, I kinda want one more step.
Rather than wiring up automatically by type, I would rather:
Create a test-applicationContext.xml which has the wiring of test classes: E.g.
<bean id="addSiteTest" class="foo.web.actions.AddSiteTest">
<property name="action"><ref bean="addSiteAction"/></property>
</bean>
Create one get/setAction in the BaseTestCase:
private Action action;
public Action getAction() {
return action;
}
public void setAction(Action action) {
this.action = action;
}
With this configuration all of my tests could delve and grab the action via getAction() without having to do anything else. For this to work, I am basically asking Spring for an API like:
injectDependencies(Object toInject, Spring beanIdWithWhichToGetDependenciesFrom, ...);
E.g.
injectDependencies(this, getMyBeanId(), ...);
Then in the given class you could override getMyBeanId(), or you could default to "my class name with the first letter lower-cased" for ease of use :)
NOTE: You can auto-wire by name:
Declare protected variables of the required type which match named beans in the context. This is autowire by name, rather than type. This approach is based on an approach originated by Ara Abrahmian. Setter Dependency Injection is the default: set the populateProtectedVariables property to true in the constructor to switch on Field Injection.
Anyway, I am just glad to have Spring watching my back on the testing side too...