lundi 28 septembre 2009

Unit testing Struts2 actions with Spring

For one of the projects on which I am working, I as looking for a solution to test the Struts2 actions while using the Spring plugin for Struts2.

There is a good article here "http://depressedprogrammer.wordpress.com/2007/06/18/unit-testing-struts-2-actions-spring-junit/", but this solution has one major problem, it tests the action alone, not the action inside the whole Struts2 request process.

So I extended the proposed solution to have a full solution to test the actions inside the Struts2 request process, including the interceptors stack, and here is the result :

/**
 */
public class AbstractActionTest extends TestCase {

    /**
     */
    private final ActionProxyFactory actionProxyFactory;

    /**
     */
    private MockHttpSession session;

    /**
     * @param configLocations
     */
    public AbstractActionTest(final String configLocations) {
        final MockServletContext servletContext = new MockServletContext();
        servletContext.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,
            configLocations);

        final ContextLoader contextLoader = new ContextLoader();
        contextLoader.initWebApplicationContext(servletContext);

        final Map initParams = new Hashtable();
        final Dispatcher dispatcher =
            new Dispatcher(servletContext, initParams);
        dispatcher.init();
        Dispatcher.setInstance(dispatcher);

        final Container container = dispatcher.getContainer();
        this.actionProxyFactory =
            container.getInstance(ActionProxyFactory.class);

        this.session = new MockHttpSession();

        /*
         * When testing with mock services, reinitialize the data between each
         * test.
         */
        // MockData.clear();
        // MockData.load();
    }

    /**
     * @param namespace
     * @param name
     * @param parameters
     * @return a string representing the logical result of the execution.
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    protected String executeAction(final String namespace, final String name,
    final Map parameters) throws Exception {
        final ActionProxy actionProxy =
            this.actionProxyFactory.createActionProxy(namespace, name, null,
                null, true, false);
        final ActionInvocation actionInvocation = actionProxy.getInvocation();

        final ActionContext actionContext =
            actionInvocation.getInvocationContext();

        actionContext.setParameters(parameters);

        ActionContext.setContext(actionContext);

        final MockHttpServletRequest request = new MockHttpServletRequest();
        request.setSession(this.session);
        final MockHttpServletResponse response = new MockHttpServletResponse();

        ServletActionContext.setRequest(request);
        ServletActionContext.setResponse(response);

        final String result = actionProxy.execute();

        // In case the session has been inactivated, we need to reassociate it.
        this.session = (MockHttpSession) request.getSession(true);

        return result;
    }

    /**
     * Find a value by evaluating the given expression against the stack in the
     * default search order.
     * 
     * @param expr
     * @return the result of evaluating the expression
     */
    protected Object findValueStackValue(final String expr) {
        final ActionContext actionContext = ActionContext.getContext();
        final ValueStack valueStack = actionContext.getValueStack();
        return valueStack.findValue(expr);
    }

    /**
     * Returns the object bound with the specified name in this session, or null
     * if no object is bound under the name.
     * 
     * @param name
     * @return the object with the specified name
     */
    protected Object getSessionValue(final String name) {
        return this.session.getAttribute(name);
    }

    /**
     * @param name
     * @param value
     */
    protected void setSessionValue(final String name, final Object value) {
        this.session.setAttribute(name, value);
    }
}

2 commentaires:

Unknown a dit…

How to use it to create a class to test login action.

Cimballi a dit…

Hey Arindam, here is an example of how to test a Struts2 action using the above mentioned class :

/**
*/
public class XxxActionTest extends AbstractActionTest {

/**
*/
private static final String ACTION_NAME = "Xxx";

/**
*/
public XxxActionTest() {
super(CONFIG_LOCATIONS);
}

/**
* @throws Exception
*/
@Test
public void testExecute() throws Exception {
final Map<String, Object> parameters = new Hashtable<String, Object>();
parameters.put("xxx", "xxx");

final String result =
super.executeAction(ACTION_NAMESPACE, ACTION_NAME, parameters);
assertEquals(Action.SUCCESS, result);
assertNotNull(super
.getSessionValue(XXX));
}
}

Sorry for the formatting, I'm not expert in Blogger...