I recently worked on a JSF-based application that was integrated with Spring, both the Core Framework as well as the Spring Security Framework. I certainly don’t have it all mastered, but the experience has given me multiple topics to blog about! We decided to let Spring manage all of the beans, rather than making JSF manage some of the beans and Spring manager the rest. I liked the consistency and thought it would make unit testing easier. Spring provides two basic scopes, singleton and prototype. When you add Spring Security into the mix, you get two additional scopes, one for session and another for request.
This should have been no big deal. To my surprise, when I wrote my first unit test, I discovered the session and request scopes were not supported; the bean factory threw an unsupported scope exception when I asked for the bean. Googling did not lead to any exact answer, but I found lots of examples; many developers seem to implement a thread scope. I simplified one of the many implementations I found, as I only need to support unit testing.
The first step is to build your own class that implements the Spring Scope interface. I created a generic class, such that I could use it for any custom scope.
public class CustomScopeHandler implements Scope {
private final Map<String, Object> hBeans = new HashMap<String, Object>();
public Object get(final String name, final ObjectFactory factory) {
Object result = null;
if (hBeans.containsKey(name)) {
result = hBeans.get(name);
}
else {
result = factory.getObject();
hBeans.put(name, result);
}
return result;
}
public Object remove(final String name) {
Object result = null;
if (hBeans.containsKey(name)) {
result = hBeans.get(name);
hBeans.remove(name);
}
return result;
}
public String getConversationId() {
Assert.state(true, “Needs to be implmented – ConversationId”);
return “N/A”;
}
@SuppressWarnings(“PMD.DoNotUseThreads”)
public void registerDestructionCallback(final String name, final Runnable callback) {
Assert.state(true, “Needs to be implmented – DestructionCallback”);
}
}
The next step is to add your custom scopes to the IoC container. Using the Spring CustomScoptConfigurer class, you can easily add as many scopes as you like. Pretty simple! Now you can execute your unit tests as you would have initially expected. One thing to be aware of, this implementation treats beans as if they are singletons. You could easily eliminate the Map and return back new instances on each invocation. It all depends on your specific needs, mine were pretty simple.
<bean class=”org.springframework.beans.factory.config.CustomScopeConfigurer”>
<property name=”scopes”>
<map>
<entry key=”request”>
<bean class=”test.scope.CustomScopeHandler” />
</entry>
<entry key=”session”>
<bean class=”test.scope.CustomScopeHandler” />
</entry>
</map>
</property>
</bean>