I would like to make use of request scoped beans in my app. I use JUnit4 for testing. If I try to create one in a test like this:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
public class TestScopedBeans {
protected final static Logger logger = Logger
.getLogger(TestScopedBeans.class);
@Resource
private Object tObj;
@Test
public void testBean() {
logger.debug(tObj);
}
@Test
public void testBean2() {
logger.debug(tObj);
}
With the following bean definition:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="java.lang.Object" id="tObj" scope="request" />
</beans>
And I get:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gov.nasa.arc.cx.sor.query.TestScopedBeans': Injection of resource fields failed; nested exception is java.lang.IllegalStateException: No Scope registered for scope 'request'
<...SNIP...>
Caused by: java.lang.IllegalStateException: No Scope registered for scope 'request'
So I found this blog that seemed helpful: http://www.javathinking.com/2009/06/no-scope-registered-for-scope-request_5.html
But I noticed he uses AbstractDependencyInjectionSpringContextTests which seems to be deprecated in Spring 3.0. I use Spring 2.5 at this time, but thought it shouldn't be too hard to switch this method to use AbstractJUnit4SpringContextTests as the docs suggest (ok the docs link to the 3.8 version but I'm using 4.4). So I change the test to extend AbstractJUnit4SpringContextTests... same message. Same problem. And now the prepareTestInstance() method I want to override is not defined. OK, maybe I'll put those registerScope calls somewhere else... So I read more about TestExecutionListeners and think that would be better since I don't want to have to inherit the spring package structure. So I changed my Test to:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
@TestExecutionListeners({})
public class TestScopedBeans {
expecting I would have to create a custom listener but I when I ran it. It works! Great, but why? I don't see where any of the stock listeners are registering request scope or session scope, and why would they? there's nothing to say I want that yet, this might not be a Test for Spring MVC code...
Test Request-Scoped Beans with Spring explains very well how to register and create a custom scope with Spring.
In a nutshell, as Ido Cohn explained, it's enough to add the following to the text context configuration:
Instead of using the predefined SimpleThreadScope, based on ThreadLocal, it's also easy to implement a Custom one, as explained in the article.
NOT reading the docs sometimes drives one crazy. Almost.
If you are using shorter-lived beans (request scope for example), you most likely also need to change your lazy init default! Otherwise the WebAppContext will fail to load and tell you something about missing request scope, which is of course missing, because the context is still loading!
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-lazy-init
The Spring guys should definitely put that hint into their exception message...
If you don't want to change the default, there is also the annotation way: put "@Lazy(true)" after @Component etc. to make singletons initialize lazy and avoid instantiating request-scoped beans too early.
MariuszS' solution works, except I couldn't get the transaction committed properly.
It seems the newly released 3.2 has finally made testing request/session scoped beans first class citizens. Here's a couple of blogs for more details.
Rossen Stoyanchev's Spring Framework 3.2 RC1: Spring MVC Test Framework
Sam Brannen's Spring Framework 3.2 RC1: New Testing Features
Solution for Spring 3.2 or newer
Spring starting with version 3.2 provides support for session/request scoped beans for integration testing.
Read more: Request and Session Scoped Beans
Solution for Spring before 3.2 with listener
WebContextTestExecutionListener.java
Solution for Spring before 3.2 with custom scopes
TestConfig.java
or with xml configuration
test-config.xml
Source code
Source code for all presented solutions:
A solution, tested with Spring 4, for when you require request-scoped beans but aren't making any requests via
MockMVC
, etc.This is still an open issue:
https://jira.springsource.org/browse/SPR-4588
I was able to get this to work (mostly) by defining a custom context loader as outlined in
http://forum.springsource.org/showthread.php?p=286280