Page 1 of 2 12 LastLast
Results 1 to 10 of 13

Thread: Testing scope=session

  1. #1
    Join Date
    Aug 2006
    Posts
    7

    Default Testing scope=session

    I have a session object that is injected into my controller (Spring MVC).

    I am having great difficulty writing an integration test for it.

    I can configure my WebApplicationContext just fine, but when I try and run my controller test I get the following error

    Code:
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name '__shoppingBasket': Scope 'session' is not active; nested exception is java.lang.IllegalStateException: No thread-bound request: use RequestContextFilter
    Caused by: java.lang.IllegalStateException: No thread-bound request: use RequestContextFilter
    	at org.springframework.web.context.scope.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:60)
    	at org.springframework.web.context.scope.SessionScope.get(SessionScope.java:77)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:274)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:153)
    	at org.springframework.aop.target.AbstractPrototypeBasedTargetSource.newPrototypeInstance(AbstractPrototypeBasedTargetSource.java:58)
    	at org.springframework.aop.target.PrototypeTargetSource.getTarget(PrototypeTargetSource.java:30)
    	at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.getTarget(Cglib2AopProxy.java:673)
    	at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:624)
    	at com.au.odin.web.support.ShoppingBasketBean$$EnhancerByCGLIB$$1a85fb85.add(<generated>)
    	at com.au.odin.web.spring.ShoppingBasketController.handleShop(ShoppingBasketController.java:45)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    	at java.lang.reflect.Method.invoke(Method.java:585)
    	at org.springframework.web.servlet.mvc.multiaction.MultiActionController.invokeNamedMethod(MultiActionController.java:434)
    	at org.springframework.web.servlet.mvc.multiaction.MultiActionController.handleRequestInternal(MultiActionController.java:372)
    	at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
    	at com.au.odin.web.spring.ShoppingBasketControllerTest$1.onRequest(ShoppingBasketControllerTest.java:43)
    	at com.au.commons.test.spring.ControllerTestsHelper.submitRequest(ControllerTestsHelper.java:66)
    	at com.au.commons.test.dbunit.spring.ControllerTests.submitRequest(ControllerTests.java:100)
    	at com.au.commons.test.dbunit.spring.ControllerTests.submitRequest(ControllerTests.java:77)
    	at com.au.commons.test.dbunit.spring.HibernateControllerTests.submitRequest(HibernateControllerTests.java:53)
    	at com.au.odin.web.spring.ShoppingBasketControllerTest.testAddItem(ShoppingBasketControllerTest.java:38)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    	at java.lang.reflect.Method.invoke(Method.java:585)
    	at junit.framework.TestCase.runTest(TestCase.java:154)
    	at junit.framework.TestCase.runBare(TestCase.java:127)
    	at org.springframework.test.ConditionalTestCase.runBare(ConditionalTestCase.java:69)
    	at junit.framework.TestResult$1.protect(TestResult.java:106)
    	at junit.framework.TestResult.runProtected(TestResult.java:124)
    	at junit.framework.TestResult.run(TestResult.java:109)
    	at junit.framework.TestCase.run(TestCase.java:118)
    	at junit.framework.TestSuite.runTest(TestSuite.java:208)
    	at junit.framework.TestSuite.run(TestSuite.java:203)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
    This is how I instantiate my WebApplicationContext.
    Code:
            String path[] = {"applicationContext.xml"            
                };
            
            applicationContext = new XmlWebApplicationContext();
            applicationContext.setConfigLocations(path);
            applicationContext.setServletContext(new MockServletContext(""));
            
            
            applicationContext.refresh();
    I have discovered the MockFilterConfig but am not sure about how to make the WebApplicationContext aware of it.

  2. #2
    Join Date
    Nov 2005
    Location
    Reutlingen, Germany
    Posts
    2,098

    Default

    For request- or session-scoped beans you do not only need a web-aware ApplicationContext, but also you need to imitate the functionality of Spring's RequestContextListener, i.e. providing a ServletRequest.

    I don't know how easy this is and what will get a best practice in future. I for myself replace scoped beans for my testcases with pure singletons and switch the config between tests and webapp.

    Jörg

  3. #3
    Join Date
    Aug 2006
    Posts
    7

    Default

    Thanks for the reply Jörg but I would like to be able to implement this functionality in my controller.

    We use continuous integration at my current client and it would be nice to not "break the build". It is also pretty tedious to keep changing your config file.

  4. #4
    Join Date
    Nov 2005
    Location
    Reutlingen, Germany
    Posts
    2,098

    Default

    Quote Originally Posted by CodeKing
    It is also pretty tedious to keep changing your config file.
    Of course I don't do this from hand. There are different techniques known to change the config in different environments, e.g. with different property files.

    I have separated the web-specific stuff into its own config file. So in the test environment I can provide a different config file on the classpath than in the web environment.

    Jörg

  5. #5
    Join Date
    Jun 2006
    Posts
    11

    Default

    Quote Originally Posted by Jörg Heinicke View Post

    I don't know how easy this is and what will get a best practice in future. I for myself replace scoped beans for my testcases with pure singletons and switch the config between tests and webapp.
    I have been using Spring 2.0 in some projects and have started using the session scope more extensively. I have a set of integration test cases that initialize the spring context and am performing integration tests on a bean that is Configurable with injected Session scoped beans. I saw this post as really the only thing I found to assist with performing integration tests with session scoped beans, and found an alternative way of enabling these to function properly.

    In my base integration test case, when I am initializing the context files I do some extra work to create a WebApplicationContext as well, so I am already overriding the loadContextLocations method of AbstractTransactionalDataSourceSpringContextTests. In that method after I initialize the context that contains my session scoped beans I simply add the below method:

    Code:
    applicationContext.getBeanFactory().registerScope("session", new SessionScope());
    You will need to be cognizant if you have hierarchical contexts, as this needs to be set on all contexts that you create that contain session scoped beans. In the onSetupBeforeTransaction method I have:

    Code:
    request = new MockHttpServletRequest();
    RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
    In the corresponding tear down method, I'm also setting the requestAttributes to null because I believe they are in a ThreadLocal object. With those couple steps I am now able to use session scoped beans in my integration tests, without having to swap out alternate configurations for testing; I find I already have to do that enough that it's becoming difficult to consider these integration tests

  6. #6
    Join Date
    Apr 2007
    Location
    Adrogué, Buenos Aires, Argentina
    Posts
    34

    Default

    There should be Some TestCase subclass that do the same job as the RequestContextListener do for initialization.
    I suggest doing something like this (I did it for my project):
    Code:
    public class WebAwareTestCase extends AbstractTransactionalDataSourceSpringContextTests {
    
      ...
    
      public final void runBare() throws Throwable {
        this.initializeContext();
        this.setUp();
        try {
          this.runTest();
        } finally {
          this.tearDown();
          this.finalizeContext();
        }
      }
    
      private void initializeContext() {
        this.setHttpServletRequest(new MockHttpServletRequest());
        ServletRequestAttributes attributes = new ServletRequestAttributes(this
            .getHttpServletRequest());
        this.getHttpServletRequest().setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE,
            attributes);
        LocaleContextHolder.setLocale(this.getHttpServletRequest().getLocale());
        RequestContextHolder.setRequestAttributes(attributes);
        if (logger.isDebugEnabled()) {
          logger.debug("Bound request context to thread: "
              + this.getHttpServletRequest());
        }
      }
    
      private void finalizeContext() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) this
            .getHttpServletRequest().getAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE);
        ServletRequestAttributes threadAttributes = (ServletRequestAttributes) RequestContextHolder
            .getRequestAttributes();
        if (threadAttributes != null) {
          // We're assumably within the original request thread...
          if (attributes == null) {
            attributes = threadAttributes;
          }
          RequestContextHolder.setRequestAttributes(null);
          LocaleContextHolder.setLocale(null);
        }
        if (attributes != null) {
          attributes.requestCompleted();
          if (logger.isDebugEnabled()) {
            logger.debug("Cleared thread-bound request context: "
                + this.getHttpServletRequest());
          }
        }
      }
    
      protected ConfigurableApplicationContext createApplicationContext(
          String[] locations) {
        GenericWebApplicationContext context = new GenericWebApplicationContext();
        context.setServletContext(new MockServletContext());
        customizeBeanFactory(context.getDefaultListableBeanFactory());
        new XmlBeanDefinitionReader(context).loadBeanDefinitions(locations);
        context.refresh();
        return context;
      }
    }
    What do you think about adding it as a requirement for new versions?

    Leandro

  7. #7
    Join Date
    Nov 2005
    Location
    Reutlingen, Germany
    Posts
    2,098

    Default

    The cases where you need session-scoped beans in your test should be rather rare. So in my opinion it does not make much sense to apply this to all tests.

    Joerg
    This post can contain insufficient information.

  8. #8
    Join Date
    Apr 2007
    Location
    Adrogué, Buenos Aires, Argentina
    Posts
    34

    Default

    Ok, but if you use session or request scoped beans in your project and load the same files for tests, the application context must have the session or request scopes registered... If not, an unregistered scope exception will be thrown...
    I´m right?

    Leandro

  9. #9
    Join Date
    Nov 2005
    Location
    Reutlingen, Germany
    Posts
    2,098

    Default

    My point is that you usually do unit tests. And you don't need to test the scoping again. I use these scopes in my application too, but I have set up my tests to inject plain objects instead of scoped proxies.

    In the cases you really want to test the scoping or do integration tests you need indeed to register the scopes.

    Does this answer your question?

    Joerg
    This post can contain insufficient information.

  10. #10
    Join Date
    Apr 2007
    Location
    Adrogué, Buenos Aires, Argentina
    Posts
    34

    Default

    Quote Originally Posted by Jörg Heinicke View Post
    My point is that you usually do unit tests. And you don't need to test the scoping again.
    Sure, I suppose it´s tested by Spring.

    Quote Originally Posted by Jörg Heinicke View Post
    I use these scopes in my application too, but I have set up my tests to inject plain objects instead of scoped proxies.
    That is what I don´t want / have to do. To define different configuration for application and tests.

    Quote Originally Posted by Jörg Heinicke View Post
    In the cases you really want to test the scoping or do integration tests you need indeed to register the scopes.
    This is my case, I´m doing integration tests...

    I think adding to the framework a TestCase subclass that helps doing integration tests using scoped beans is useful.
    What do you think?

    Regards
    Leandro

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •