Fork me on GitHub

Programming, Internet & more

Test Request-Scoped Beans with Spring

Spring makes it very easy to write test with jUnit. There are a lot of helpers like SpringJUnit4ClassRunner which allows you to write tests for spring-managed objects.

But assume you have a web application and you have annotated most of your beans/controllers with @Scope(“request”) or @Scope(“session”). If you try to write a test for this sort of objects you will get an Exception like this:

java.lang.IllegalStateException: No Scope registered for scope 'request'

This is because a session or request scope will only work in a web environment. In a jUnit test class there is no such environment. How can to solve this problem?

Application Setup

I assume that there is one managed-bean which is request-scoped and does some basic calculations.

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("request")
public class MyManagedBean {
   
    public int addNumbers(int a, int b) {
        return a + b;
    }
}

There is also a unit-test to test the managed-bean.

import junit.framework.Assert;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration("/test-context.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class MyManagedBeanTest {

    @Autowired
    private MyManagedBean bean;
   
    @Test
    public void testAddNumbers() {
        Assert.assertEquals(3, bean.addNumbers(1, 2));
        Assert.assertEquals(0, bean.addNumbers(0, 0));
        Assert.assertEquals(-1, bean.addNumbers(0, -1));
        Assert.assertEquals(-2, bean.addNumbers(-1, -1));
    }
}

f you try to run this test it will give you the described IllegalStateException.

Create a custom scope

The easiest way to solve this problem is to create your own custom scope which is some kind of a mock. The scope holds your bean instances and delivers them to spring when needed.

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

public class CustomScope implements Scope {

    private final Map<String , Object> beanMap = new HashMap<String , Object>();
   
    public Object get(String name, ObjectFactory<?> factory) {
        Object bean = beanMap.get(name);
        if (null == bean) {
            bean = factory.getObject();
            beanMap.put(name, bean);
        }
        return bean;
    }

    public String getConversationId() {
        // not needed
        return null;
    }

    public void registerDestructionCallback(String arg0, Runnable arg1) {
        // not needed
    }

    public Object remove(String obj) {
        return beanMap.remove(obj);
    }

    public Object resolveContextualObject(String arg0) {
        // not needed
        return null;
    }
}

Register the custom scope

To use the custom scope we have to register it in our spring test-configuration. In this config we map the session and request scopes to the new custom scope.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- Scans within the base package of the application for @Components to
        configure as beans -->
    <context:component-scan base-package="de.slackspace.tutorials" />  
   
    <!-- Needed to test session/request scoped beans -->
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="session">
                    <bean class="de.slackspace.tutorials.test.CustomScope" />
                </entry>
                <entry key="request">
                    <bean class="de.slackspace.tutorials.test.CustomScope" />
                </entry>
            </map>
        </property>
    </bean>
</beans>

Run the tests

The unit-test from above which failed before, runs now smoothly.

Source-Code

You can find a full working example at GitHub.

Category: spring
-->

3 Comments

  1. Posted July 14, 2012 at 10:08 | Permalink

    I was reading through some of your content on this site and I believe this website is really informative! Keep on posting.

  2. Posted August 17, 2012 at 01:45 | Permalink

    You got a very great website, Glad I found it through yahoo.

  3. shirish sahu
    Posted December 18, 2015 at 16:51 | Permalink

    Thanks alot for providing implementations and explanation ..

Post a Comment

Your email is kept private. Required fields are marked *

*
*