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

Thread: Integration tests with mocked session

  1. #1
    Join Date
    Jan 2007
    Posts
    8

    Default 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 ?

  2. #2
    Join Date
    May 2007
    Posts
    10

    Default

    You need to register this scope in your tests as Spring doesn't assume your IoC container runs in a webcontext. You need to tell Spring you'd like session scope, you normally do this declaratively in your web.xml but in your test you do this programatically.

    The ConfigurableBeanFactory which the bean factory implementations implement has a method 'registerScope(String scopeName, Scope scope)'.

    So do something like factory.registerScope("session", new SessionScope());
    Do the same for request and any other non standard scope you require.

    You can also use Spring objects like MockHttpServletRequest, MockHttpServletResponse and MockHttpSession in your tests to create your http objects.

    I'd recommend creating something like an AbstractWebContextIntegrationTest (or a separate concrete class you have as an attribute) and putting this code in there so all your http dependent int tests can have this for free.

    It wouldn't be a bad thing if the Spring guys added a class like this to the test libraries...

  3. #3
    Join Date
    Jan 2007
    Posts
    8

    Default

    The main problem im facing is the fact that prior to any tests the applicationContext is read and created.
    During this process spring realizes that the scope=session is not possible due to the fact that Im not running my tests within a servlet environment.

    Thus from my point of view a solution would be to intercept or override the method loadContext or loadContexts in order to register the new scope into the applicationContext.

    My problem is that I have not yet found a propery testing class where I could override this method.

    Am I completely wrong ??

    Any help is appreciated

  4. #4
    Join Date
    May 2007
    Posts
    10

    Default

    You cast the applicationContext to a ConfigurableBeanFactory in the onSetup() method, then register the custom scope(s).

    If you're saying the container is already instantiated and the failure has already happened then that would be because you're declaring your spring config files using annoations - if that's the case then you probably shouldn't use annotations here, and instead overide getConfigLocations().

  5. #5
    Join Date
    Feb 2005
    Location
    Boston, MA
    Posts
    1,142

    Default

    I'd recommend creating a XML file that is specific to your integration test and register a custom scope:

    Code:
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
      <property name="scopes">
        <map>
          <entry key="session"><bean class="TestScope"/></entry>
        </map>
      </property>
    </bean>
    Unfortunately I don't think SessionScope will be acceptable because it requires a Session, or a reasonable approximation backing it. Also, "prototype" and "singleton" are not actual scopes, so you'd need to implement one. This class is the same as a prototype. If you wanted to be more similar to a singleton, in the get method you could store the bean in a HashMap using its name. If you wanted to get fancier you could build in support to your test class have the bean last the length of a single test, so the same bean would be returned during one test.

    Code:
    import org.springframework.beans.factory.config.Scope;
    import org.springframework.beans.factory.ObjectFactory;
    
    public class TestScope implements Scope {
        public String getConversationId() {
            return null;
        }
    
        public Object get(String name, ObjectFactory objectFactory) {
            return objectFactory.getObject();
        }
    
        public Object remove(String name) {
            return null;
        }
    
        public void registerDestructionCallback(String name, Runnable callback) {
        }
    }
    Last edited by wpoitras; Mar 5th, 2008 at 11:40 AM. Reason: Missing property name
    Bill

  6. #6
    Join Date
    Jan 2007
    Posts
    8

    Default

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

  7. #7
    Join Date
    Feb 2005
    Location
    Boston, MA
    Posts
    1,142

    Default

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

  8. #8
    Join Date
    May 2007
    Posts
    10

    Default

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

  9. #9

    Default

    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.

  10. #10
    Join Date
    Mar 2008
    Posts
    3

    Default

    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

Posting Permissions

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