All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sourceforge.stripes.mock.MockRoundtrip Maven / Gradle / Ivy

/* Copyright 2005-2006 Tim Fennell
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.sourceforge.stripes.mock;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.Filter;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.controller.StripesConstants;
import net.sourceforge.stripes.controller.StripesFilter;
import net.sourceforge.stripes.util.CryptoUtil;
import net.sourceforge.stripes.validation.ValidationErrors;

/**
 * 

Mock object that attempts to make it easier to use the other Mock objects in this package * to interact with Stripes and to interrogate the results. Everything that is done in this class * is do-able without this class! It simply exists to make things a bit easier. As a result all * the methods in this class simply manipulate one or more of the underlying Mock objects. If * some needed capability is not exposed through the MockRoundtrip it is always possible to fetch * the underlying request, response and context and interact with them directly.

* *

It is worth noting that the Mock system does not process forwards, includes and * redirects. When an ActionBean (or other object) invokes the servlet APIs for any of these * actions it is recorded so that it can be reported and verified later. In the majority of cases * it should be sufficient to test ActionBeans in isolation and verify that they produced the * expected output data and/or forward/redirect. If your ActionBeans depend on being able to include * other resources before continuing, sorry - you're on your own!

* *

An example usage of this class might look like:

* *
 * MockServletContext context = ...;
 * MockRoundtrip trip = new MockRoundtrip(context, CalculatorActionBean.class);
 * trip.setParameter("numberOne", "2");
 * trip.setParameter("numberTwo", "2");
 * trip.execute();
 * CalculatorActionBean bean = trip.getActionBean(CalculatorActionBean.class);
 * Assert.assertEquals(bean.getResult(), 4, "two plus two should equal four");
 * Assert.assertEquals(trip.getDestination(), ""/quickstart/index.jsp");
 * 
* * @author Tim Fennell * @since Stripes 1.1.1 */ public class MockRoundtrip { /** Default value for the source page that generated this round trip request. */ public static final String DEFAULT_SOURCE_PAGE = "_default_source_page_"; private MockHttpServletRequest request; private MockHttpServletResponse response; private MockServletContext context; /** * Preferred constructor that will manufacture a request. Uses the ServletContext to ensure * that the request's context path matches. Pulls the UrlBinding of the ActionBean and uses * that as the requst URL. Constructs a new session for the request. * * @param context the MockServletContext that will receive this request * @param beanType a Class object representing the ActionBean that should receive the request */ public MockRoundtrip(MockServletContext context, Class beanType) { this(context, beanType, new MockHttpSession(context) ); } /** * Preferred constructor that will manufacture a request. Uses the ServletContext to ensure * that the request's context path matches. Pulls the UrlBinding of the ActionBean and uses * that as the requst URL. Constructs a new session for the request. * * @param context the MockServletContext that will receive this request * @param beanType a Class object representing the ActionBean that should receive the request */ public MockRoundtrip(MockServletContext context, Class beanType, MockHttpSession session) { this(context, getUrlBinding(beanType, context), session); } /** * Constructor that will create a requeset suitable for the provided servlet context and * URL. Note that in general the contructors that take an ActionBean Class object are preferred * over those that take a URL. Constructs a new session for the request. * * @param context the MockServletContext that will receive this request * @param actionBeanUrl the url binding of the action bean */ public MockRoundtrip(MockServletContext context, String actionBeanUrl) { this(context, actionBeanUrl, new MockHttpSession(context)); } /** * Constructor that will create a requeset suitable for the provided servlet context and * URL. Note that in general the contructors that take an ActionBean Class object are preferred * over those that take a URL. The request will use the provided session instead of creating * a new one. * * @param context the MockServletContext that will receive this request * @param actionBeanUrl the url binding of the action bean * @param session an instance of MockHttpSession to use for the request */ public MockRoundtrip(MockServletContext context, String actionBeanUrl, MockHttpSession session) { // Look for a query string and parse out the parameters if one is present String path = actionBeanUrl; SortedMap> parameters = null; int qmark = actionBeanUrl.indexOf("?"); if (qmark > 0) { path = actionBeanUrl.substring(0, qmark); if (qmark < actionBeanUrl.length()) { String query = actionBeanUrl.substring(qmark + 1); if (query != null && query.length() > 0) { parameters = new TreeMap>(); for (String kv : query.split("&")) { String[] parts = kv.split("="); String key, value; if (parts.length == 1) { key = parts[0]; value = null; } else if (parts.length == 2) { key = parts[0]; value = parts[1]; } else { key = value = null; } if (key != null) { List values = parameters.get(key); if (values == null) values = new ArrayList(); values.add(value); parameters.put(key, values); } } } } } this.context = context; this.request = new MockHttpServletRequest("/" + context.getServletContextName(), path); this.request.setSession(session); this.response = new MockHttpServletResponse(); setSourcePage(DEFAULT_SOURCE_PAGE); // Add any parameters that were embedded in the given URL if (parameters != null) { for (Map.Entry> entry : parameters.entrySet()) { for (String value : entry.getValue()) { addParameter(entry.getKey(), value); } } } } /** Get the servlet request object to be used by this round trip */ public MockHttpServletRequest getRequest() { return request; } /** Set the servlet request object to be used by this round trip */ protected void setRequest(MockHttpServletRequest request) { this.request = request; } /** Get the servlet response object to be used by this round trip */ public MockHttpServletResponse getResponse() { return response; } /** Set the servlet response object to be used by this round trip */ protected void setResponse(MockHttpServletResponse response) { this.response = response; } /** Get the ActionBean context to be used by this round trip */ public MockServletContext getContext() { return context; } /** Set the ActionBean context to be used by this round trip */ protected void setContext(MockServletContext context) { this.context = context; } /** * Sets the named request parameter to the value or values provided. Any existing values are * wiped out and replaced with the value(s) provided. */ public void setParameter(String name, String... value) { this.request.getParameterMap().put(name, value); } /** * Adds the value provided to the set of values for the named request parameter. If one or * more values already exist they will be retained, and the new value will be appended to the * set of values. */ public void addParameter(String name, String... value) { if (this.request.getParameterValues(name) == null) { setParameter(name, value); } else { String[] oldValues = this.request.getParameterMap().get(name); String[] combined = new String[oldValues.length + value.length]; System.arraycopy(oldValues, 0, combined, 0, oldValues.length); System.arraycopy(value, 0, combined, oldValues.length, value.length); setParameter(name, combined); } } /** * All requests to Stripes that can generate validation errors are required to supply a * request parameter telling Stripes where the request came from. If you do not supply a * value for this parameter then the value of MockRoundTrip.DEFAULT_SOURCE_PAGE will be used. */ public void setSourcePage(String url) { if (url != null) { url = CryptoUtil.encrypt(url); } setParameter(StripesConstants.URL_KEY_SOURCE_PAGE, url); } /** * Executes the request in the servlet context that was provided in the constructor. If the * request throws an Exception then that will be thrown from this method. Otherwise, once the * execution has completed you can use the other methods on this class to examine the outcome. */ public void execute() throws Exception { this.context.acceptRequest(this.request, this.response); } /** * Executes the request in the servlet context that was provided in the constructor. Sets up * the request so that it mimics the submission of a specific event, named by the 'event' * parameter to this method. If the request throws an Exception then that will be thrown from * this method. Otherwise, once the execution has completed you can use the other methods on * this class to examine the outcome. */ public void execute(String event) throws Exception { setParameter(event, ""); execute(); } /** * Gets the instance of the ActionBean type provided that was instantiated by Stripes to * handle the request. If a bean of this type was not instantiated, this method will * return null. * * @param type the Class object representing the ActionBean type expected * @return the instance of the ActionBean that was created by Stripes */ @SuppressWarnings("unchecked") public A getActionBean(Class type) { A bean = (A) this.request.getAttribute(getUrlBinding(type, this.context)); if (bean == null) { bean = (A) this.request.getSession().getAttribute(getUrlBinding(type, this.context)); } return bean; } /** * Gets the (potentially empty) set of Validation Errors that were produced by the request. */ public ValidationErrors getValidationErrors() { ActionBean bean = (ActionBean) this.request.getAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN); return bean.getContext().getValidationErrors(); } /** * Gets, as bytes, any data that was written to the output stream associated with the * request. Note that since the Mock system does not write standard HTTP response information * (headers etc.) to the output stream, this will be exactly what was written by the * ActionBean. */ public byte[] getOutputBytes() { return this.response.getOutputBytes(); } /** * Gets, as a String, any data that was written to the output stream associated with the * request. Note that since the Mock system does not write standard HTTP response information * (headers etc.) to the output stream, this will be exactly what was written by the * ActionBean. */ public String getOutputString() { return this.response.getOutputString(); } /** * Gets the URL to which Stripes was directed after invoking the ActionBean. Assumes that * the request was either forwarded or redirected exactly once. If the request was forwarded * then the forwarded URL will be returned verbatim. If the response was redirected and the * redirect URL was within the same web application, then the URL returned will exclude the * context path. I.e. the URL returned will be the same regardless of whether the page was * forwarded to or redirected to. */ public String getDestination() { String forward = this.request.getForwardUrl(); String redirect = this.response.getRedirectUrl(); if (forward != null) { return forward; } else if (redirect != null) { String contextPath = this.request.getContextPath(); if (contextPath.length() > 1 && redirect.startsWith(contextPath + '/')) redirect = redirect.substring(contextPath.length()); } return redirect; } /** If the request resulted in a forward, returns the URL that was forwarded to. */ public String getForwardUrl() { return this.request.getForwardUrl(); } /** * If the request resulted in a redirect, returns the URL that was redirected to. Unlike * getDestination(), the URL in this case will be the exact URL that would have been sent to * the browser (i.e. including the servlet context). */ public String getRedirectUrl() { return this.response.getRedirectUrl(); } /** * A helper method that fetches the UrlBinding of a class in the manner it would * be interpreted by the current context configuration. */ private static String getUrlBinding(Class clazz, MockServletContext context) { List filters = context.getFilters(); for (Filter filter : filters) { if (filter instanceof StripesFilter) { return ((StripesFilter) filter).getInstanceConfiguration() .getActionResolver().getUrlBinding(clazz); } } return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy