Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

request scoped beans in spring testing

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...

like image 621
harschware Avatar asked Mar 09 '10 18:03

harschware


People also ask

What is request bean scope in Spring?

The request scope creates a bean instance for a single HTTP request, while the session scope creates a bean instance for an HTTP Session. The application scope creates the bean instance for the lifecycle of a ServletContext, and the websocket scope creates it for a particular WebSocket session.

How do you inject request in scoped bean?

As singleton beans are injected only once per their lifetime you need to provide scoped beans as proxies which takes care of that. @RequestScope is a meta-annotation on @Scope that 1) sets the scope to "request" and 2) sets the proxyMode to ScopedProxyMode.

What is the difference between prototype and request scope in Spring?

Prototype scope creates a new instance every time getBean method is invoked on the ApplicationContext. Whereas for request scope, only one instance is created for an HttpRequest.

What is @scope annotation in Spring?

When used as a type-level annotation in conjunction with @Component , @Scope indicates the name of a scope to use for instances of the annotated type. When used as a method-level annotation in conjunction with @Bean , @Scope indicates the name of a scope to use for the instance returned from the method.


2 Answers

Solution for Spring 3.2 or newer

Spring starting with version 3.2 provides support for session/request scoped beans for integration testing.

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestConfig.class) @WebAppConfiguration public class SampleTest {      @Autowired WebApplicationContext wac;      @Autowired MockHttpServletRequest request;      @Autowired MockHttpSession session;          @Autowired MySessionBean mySessionBean;      @Autowired MyRequestBean myRequestBean;      @Test     public void requestScope() throws Exception {         assertThat(myRequestBean)            .isSameAs(request.getAttribute("myRequestBean"));         assertThat(myRequestBean)            .isSameAs(wac.getBean("myRequestBean", MyRequestBean.class));     }      @Test     public void sessionScope() throws Exception {         assertThat(mySessionBean)            .isSameAs(session.getAttribute("mySessionBean"));         assertThat(mySessionBean)            .isSameAs(wac.getBean("mySessionBean", MySessionBean.class));     } } 

Read more: Request and Session Scoped Beans


Solution for Spring before 3.2 with listener

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestConfig.class) @TestExecutionListeners({WebContextTestExecutionListener.class,         DependencyInjectionTestExecutionListener.class,         DirtiesContextTestExecutionListener.class}) public class SampleTest {     ... } 

WebContextTestExecutionListener.java

public  class WebContextTestExecutionListener extends AbstractTestExecutionListener {     @Override     public void prepareTestInstance(TestContext testContext) {         if (testContext.getApplicationContext() instanceof GenericApplicationContext) {             GenericApplicationContext context = (GenericApplicationContext) testContext.getApplicationContext();             ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();             beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST,                     new SimpleThreadScope());             beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION,                     new SimpleThreadScope());         }     } } 

Solution for Spring before 3.2 with custom scopes

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestConfig.class, locations = "test-config.xml") public class SampleTest {  ...  } 

TestConfig.java

@Configuration @ComponentScan(...) public class TestConfig {      @Bean     public CustomScopeConfigurer customScopeConfigurer(){         CustomScopeConfigurer scopeConfigurer = new CustomScopeConfigurer();          HashMap<String, Object> scopes = new HashMap<String, Object>();         scopes.put(WebApplicationContext.SCOPE_REQUEST,                 new SimpleThreadScope());         scopes.put(WebApplicationContext.SCOPE_SESSION,                 new SimpleThreadScope());         scopeConfigurer.setScopes(scopes);          return scopeConfigurer  } 

or with xml configuration

test-config.xml

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">     <property name="scopes">         <map>             <entry key="request">                 <bean class="org.springframework.context.support.SimpleThreadScope"/>             </entry>         </map>         <map>             <entry key="session">                 <bean class="org.springframework.context.support.SimpleThreadScope"/>             </entry>         </map>     </property> </bean> 

Source code

Source code for all presented solutions:

  • https://github.com/mariuszs/spring-test-web
like image 142
MariuszS Avatar answered Sep 20 '22 22:09

MariuszS


I've tried several solutions, including @Marius's solution with the "WebContextTestExecutionListener", but it didn't work for me, as this code loaded the application context before creating the request scope.

The answer that helped me in the end is not a new one, but it's good: http://tarunsapra.wordpress.com/2011/06/28/junit-spring-session-and-request-scope-beans/

I simply added the following snippet to my (test) application context:

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">     <property name="scopes">         <map>             <entry key="request">                 <bean class="org.springframework.context.support.SimpleThreadScope"/>             </entry>         </map>     </property> </bean> 

Good luck!

like image 34
Ido Cohn Avatar answered Sep 20 '22 22:09

Ido Cohn