Announcement Announcement Module
Collapse
No announcement yet.
Integration tests with mocked session Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • loumaus
    started a topic Integration tests with mocked session

    Integration tests with mocked session

    I need to setup integration tests testing a session scoped bean.
    I have looked up several forum conversation but was not able to find one fitting my requirements.

    Am actually trying to setup a testing environment as follows:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations={"/spring-beans.xml"})
    @TransactionConfiguration(transactionManager="txMa nager", defaultRollback=true)
    @Transactional
    public class IntegrationTest extends AbstractTransactionalSpringContextTests {

    ...
    }

    By default running a test in this environment returns something like:
    No Scope registered for scope 'session'

    Understandable, as no request object has been created.

    Question:
    Where do I set modify the applicationContext to add the scoping and mocked servlet request ?

  • srinivas466
    replied
    great solution

    Hi,

    The below solution works fine. Thanks a lot.

    Originally posted by pbdavey View Post
    So I spent today messing around with this same stuff and came up with the following solution...

    Start with a basic Spring Test
    Code:
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations=("test-spring-config.xml"))
    @TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
    Change Context Configuration
    Code:
    @ContextConfiguration(locations=("test-spring-config.xml"), loader=TestWebSessionContextLoader.class)
    Add TestWebContextLoader.java (no session yet, but a good base to extend for request, etc) - this is basically a rip-off of the AbstractGenericContextLoader and GenericXmlContextLoader, in fact, this would all be easier if the AbstractGenericContextLoader didn't have loadContext declared as final. However, one important difference is that the context refresh is in customizeContext as it must be refreshed before registerScope is called for the bean factory.
    Code:
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.beans.factory.support.BeanDefinitionReader;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigUtils;
    import org.springframework.context.support.GenericApplicationContext;
    import org.springframework.web.context.support.GenericWebApplicationContext;
    import org.springframework.util.StringUtils;
    import org.springframework.test.context.support.AbstractContextLoader;
    import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
    
    public class TestWebContextLoader extends AbstractContextLoader {
    
    	protected static final Log logger = LogFactory
    			.getLog(TestWebContextLoader.class);
    
    	public final ConfigurableApplicationContext loadContext(
    			final String... locations) throws Exception {
    
    		if (logger.isDebugEnabled()) {
    			logger.debug("Loading ApplicationContext for locations ["
    							+ StringUtils.arrayToCommaDelimitedString(locations)
    							+ "].");
    		}
    
    		GenericWebApplicationContext context = new GenericWebApplicationContext();
    		customizeBeanFactory(context.getDefaultListableBeanFactory());
    		createBeanDefinitionReader(context).loadBeanDefinitions(locations);
    		AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
    		customizeContext(context);
    		
    		context.registerShutdownHook();
    		return context;
    	}
    
    	protected void customizeBeanFactory(
    			final DefaultListableBeanFactory beanFactory) {
    		/* no-op */
    	}
    
    	protected void customizeContext(final GenericWebApplicationContext context) {
                          /* refresh must be called when customizeContext is overriden */
    		context.refresh();
    	}
    
    	protected BeanDefinitionReader createBeanDefinitionReader(final GenericApplicationContext context) {
    		return new XmlBeanDefinitionReader(context);
    	}
    
    	@Override
    	public String getResourceSuffix() {
    		return "-context.xml";
    	}
    }
    Add Session-version of the TestWebContextLoader: TestWebSessionContextLoader.java
    Code:
    import org.springframework.web.context.support.GenericWebApplicationContext;
    
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import org.springframework.web.context.request.SessionScope;
    import org.springframework.mock.web.MockHttpServletRequest;
    import org.springframework.mock.web.MockHttpSession;
    import org.springframework.mock.web.MockServletContext;
    
    
    public class TestWebSessionContextLoader extends TestWebContextLoader {
    	
    	@Override
    	protected void customizeContext(final GenericWebApplicationContext context) {
    		MockServletContext servlet = new MockServletContext();
    		context.setServletContext(servlet);
    		
    		MockHttpServletRequest request = new MockHttpServletRequest();
    		MockHttpSession session = new MockHttpSession();
    		request.setSession(session);
    		RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
    	
    		context.refresh();
    		context.getBeanFactory().registerScope("session", new SessionScope());
    	}
    }
    Tada! You now have an ApplicationContext with a mock session. If there's any issues, let me know, but I think it works ok.

    Leave a comment:


  • cemartins
    replied
    I fixed it using this

    In context.xml:

    <bean class="org.springframework.beans.factory.config.Cu stomScopeConfigurer">
    <property name="scopes">
    <map>
    <entry key="session"><bean class="SetupSession"/></entry>
    </map>
    </property>
    </bean>

    <context:component-scan base-package="test" />

    Note the definition of CustomScopeConfigurer bean BEFORE component scan definition.

    The SetupSession class is as follows:

    import org.springframework.beans.factory.InitializingBean ;
    import org.springframework.mock.web.MockHttpServletReques t;
    import org.springframework.mock.web.MockHttpSession;
    import org.springframework.web.context.request.RequestCon textHolder;
    import org.springframework.web.context.request.ServletReq uestAttributes;
    import org.springframework.web.context.request.SessionSco pe;

    public class SetupSession extends SessionScope implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
    MockHttpServletRequest request = new MockHttpServletRequest();
    MockHttpSession session = new MockHttpSession();
    request.setSession(session);
    RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
    }
    }

    Leave a comment:


  • beezlebug
    replied
    JIRA Issue concerning this

    Hi,

    there'a a JIRA issue concerning integration testing of session scoped beans, see http://jira.springframework.org/browse/SPR-4588

    Stefan

    Leave a comment:


  • t.heuer
    replied
    The Solution of pbdavey works perfectly finte. Thanks.

    One remark, yuo can't autowire beans by method like:

    private BenutzerkontoService BenutzerkontoService;

    @Autowired
    public void setBenutzerkontoService(BenutzerkontoServiceImpl BenutzerkontoService){
    this.BenutzerkontoService = BenutzerkontoService;
    }

    but if you simply use

    @Autowired
    private BenutzerkontoService BenutzerkontoService;

    it works fine.
    Last edited by t.heuer; Jan 23rd, 2009, 09:52 AM.

    Leave a comment:


  • krueger
    replied
    The solution with a special test context in xml and a registered TestScope class worked like a charm for me! Thanks!

    Leave a comment:


  • pbdavey
    replied
    Spring + JUnit4 + Session Scope

    So I spent today messing around with this same stuff and came up with the following solution...

    Start with a basic Spring Test
    Code:
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations=("test-spring-config.xml"))
    @TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
    Change Context Configuration
    Code:
    @ContextConfiguration(locations=("test-spring-config.xml"), loader=TestWebSessionContextLoader.class)
    Add TestWebContextLoader.java (no session yet, but a good base to extend for request, etc) - this is basically a rip-off of the AbstractGenericContextLoader and GenericXmlContextLoader, in fact, this would all be easier if the AbstractGenericContextLoader didn't have loadContext declared as final. However, one important difference is that the context refresh is in customizeContext as it must be refreshed before registerScope is called for the bean factory.
    Code:
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.beans.factory.support.BeanDefinitionReader;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigUtils;
    import org.springframework.context.support.GenericApplicationContext;
    import org.springframework.web.context.support.GenericWebApplicationContext;
    import org.springframework.util.StringUtils;
    import org.springframework.test.context.support.AbstractContextLoader;
    import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
    
    public class TestWebContextLoader extends AbstractContextLoader {
    
    	protected static final Log logger = LogFactory
    			.getLog(TestWebContextLoader.class);
    
    	public final ConfigurableApplicationContext loadContext(
    			final String... locations) throws Exception {
    
    		if (logger.isDebugEnabled()) {
    			logger.debug("Loading ApplicationContext for locations ["
    							+ StringUtils.arrayToCommaDelimitedString(locations)
    							+ "].");
    		}
    
    		GenericWebApplicationContext context = new GenericWebApplicationContext();
    		customizeBeanFactory(context.getDefaultListableBeanFactory());
    		createBeanDefinitionReader(context).loadBeanDefinitions(locations);
    		AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
    		customizeContext(context);
    		
    		context.registerShutdownHook();
    		return context;
    	}
    
    	protected void customizeBeanFactory(
    			final DefaultListableBeanFactory beanFactory) {
    		/* no-op */
    	}
    
    	protected void customizeContext(final GenericWebApplicationContext context) {
                          /* refresh must be called when customizeContext is overriden */
    		context.refresh();
    	}
    
    	protected BeanDefinitionReader createBeanDefinitionReader(final GenericApplicationContext context) {
    		return new XmlBeanDefinitionReader(context);
    	}
    
    	@Override
    	public String getResourceSuffix() {
    		return "-context.xml";
    	}
    }
    Add Session-version of the TestWebContextLoader: TestWebSessionContextLoader.java
    Code:
    import org.springframework.web.context.support.GenericWebApplicationContext;
    
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import org.springframework.web.context.request.SessionScope;
    import org.springframework.mock.web.MockHttpServletRequest;
    import org.springframework.mock.web.MockHttpSession;
    import org.springframework.mock.web.MockServletContext;
    
    
    public class TestWebSessionContextLoader extends TestWebContextLoader {
    	
    	@Override
    	protected void customizeContext(final GenericWebApplicationContext context) {
    		MockServletContext servlet = new MockServletContext();
    		context.setServletContext(servlet);
    		
    		MockHttpServletRequest request = new MockHttpServletRequest();
    		MockHttpSession session = new MockHttpSession();
    		request.setSession(session);
    		RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
    	
    		context.refresh();
    		context.getBeanFactory().registerScope("session", new SessionScope());
    	}
    }
    Tada! You now have an ApplicationContext with a mock session. If there's any issues, let me know, but I think it works ok.

    Leave a comment:


  • mig
    replied
    Saw this thread and wanted to give my solution. It is an extension of the solution described above.

    What is central in the solution is that the webapplication context is recreated. It is based on the statement which I found in the Spring documentation:

    The scopes that are described in the following paragraphs are only available if you are using a web-aware Spring ApplicationContext implementation (such as XmlWebApplicationContext). If you try using these next scopes with regular Spring IoC containers such as the XmlBeanFactory or ClassPathXmlApplicationContext, you will get an IllegalStateException complaining about an unknown bean scope.

    The scopes described are for example the request, and conversation scope. This all leads to the following steps to follow:
    1. Use the XmlWebApplicationContext as an application context. This application context has the advantage that it has the request object available.
    2. Set the request object by:
      MockHttpServletRequest request = new MockHttpServletRequest();
      MockHttpSession session = new MockHttpSession();
      request.setSession(session);
      RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
    3. For this you need the spring-mock.jar for the extra mock objects

    Leave a comment:


  • rkannan_82
    replied
    A working solution

    We can override the customizeBeanFactory method and add the session scope to the default bean factory. For a session scoped bean to work properly we need to have a http request exposed to the thread.

    /**
    * Customizes the default bean factory by adding session scope and creating a mock HTTP request
    *
    * @param beanFactory - Default bean factory
    */
    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    beanFactory.registerScope("session", new SessionScope());
    MockHttpServletRequest req = new MockHttpServletRequest();
    ServletRequestAttributes attrbs = new ServletRequestAttributes(req);
    RequestContextHolder.setRequestAttributes(attrbs);
    }

    Leave a comment:


  • _paul_
    replied
    Did somebody find complete soluton for that?

    tomatrix,
    I belive your solution works but I inherit my test clasess from
    AbstractTransactionalSpringContextTests.

    and the tweak is not acceptable for me.
    Thank you,
    /Paul.

    Leave a comment:


  • tomatrix
    replied
    Leo, I had the same problem with session scope during unit testing webflow, and I found the following solution (hack?):
    At the start of a test I add a flow execution listener that reacts to the 'sessionCreated' event. Here you have access to the request context and can put things in all available scopes.
    Example:
    Code:
    public void testFoo() {
       setFlowExecutionListener(
          new FlowExecutionListenerAdapter() {
             public void sessionCreated(RequestContext ctx, FlowSession session) {
                ctx.getExternalContext().getSessionMap().put('someObjectName', new SomeObject());
             }
          }
       );
       // tests
    }
    Regards,
    Marnix

    Leave a comment:


  • leojhartiv
    replied
    Andy (and anyone else):

    Were you able to get this to work? I'm trying to do the same thing (mocking RequestScope in this case for use with request scoped bean) and I simply cannot get it to function.

    Leave a comment:


  • andymorris
    replied
    Yes I think that would fix it. Otherwise you'd need to stop using the AbstractDependencyInjectionSpringContextTests test class and instantiate the container yourself. The problem is that using that class the container is created and accessed before you register the scope.

    So either do as wpoitras says or write a standard junit test and instantiate the container yourself, though you'd need to look up the object you want to test

    Code:
    ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-beans.xml"); 
    applicationContext.getBeanFactory().registerScope("session", new SessionScope());
    
    MockHttpServletRequest request = new MockHttpServletRequest();
    MockHttpSession session = new MockHttpSession();
    request.setSession(session);
    RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
    
    SomeObject someObject = (SomeObject) applicationContext.getBean("someObject");
    That will work as I've tested it...

    Leave a comment:


  • wpoitras
    replied
    I think if you register the SessionScope in XML using CustomScopeConfigurer instead of onSetUp it might work.

    Leave a comment:


  • loumaus
    replied
    Your really a great help.

    There is one question left ..

    I have added code like:
    @Override
    protected void onSetUp() throws Exception {

    MockHttpServletRequest request = new MockHttpServletRequest();
    MockHttpSession session = new MockHttpSession();
    request.setSession(session);
    RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
    applicationContext.getBeanFactory().registerScope( "session", new SessionScope());
    }

    which should register the session scope and take care about the servlet request.
    It looks nice but the result is the same:

    "nested exception is java.lang.IllegalStateException: No Scope registered for scope 'session'"

    Is there any additional hint you could provide helping me to fix this issue ???

    Leave a comment:

Working...
X