extended Servlet API mocks for Servlet 3.0 forward compatibility as far as possible; made MockHttpServletResponse compatible with Servlet 3.0 getHeader(s) method returning Strings (SPR-8529); added getHeaderValue(s) method to MockHttpServletResponse for raw value access

This commit is contained in:
Juergen Hoeller 2011-07-15 14:16:31 +00:00
parent 4e1cb2b823
commit 18ab057e90
5 changed files with 166 additions and 61 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2011 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -71,6 +71,10 @@ class HeaderValueHolder {
return (!this.values.isEmpty() ? this.values.get(0) : null); return (!this.values.isEmpty() ? this.values.get(0) : null);
} }
public String getStringValue() {
return (!this.values.isEmpty() ? this.values.get(0).toString() : null);
}
/** /**
* Find a HeaderValueHolder by name, ignoring casing. * Find a HeaderValueHolder by name, ignoring casing.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2011 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.mock.web;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
@ -35,23 +36,23 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Vector; import java.util.Vector;
import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream; import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.LinkedCaseInsensitiveMap; import org.springframework.util.LinkedCaseInsensitiveMap;
/** /**
* Mock implementation of the {@link javax.servlet.http.HttpServletRequest} * Mock implementation of the {@link javax.servlet.http.HttpServletRequest} interface.
* interface. Supports the Servlet 2.5 API level. *
* <p> * <p>Compatible with Servlet 2.5 and partially with Servlet 3.0 (notable exceptions:
* Used for testing the web framework; also useful for testing application * the <code>getPart(s)</code> and <code>startAsync</code> families of methods).
* controllers.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Rod Johnson * @author Rod Johnson
@ -93,6 +94,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
private boolean active = true; private boolean active = true;
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// ServletRequest properties // ServletRequest properties
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -134,6 +136,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
private int localPort = DEFAULT_SERVER_PORT; private int localPort = DEFAULT_SERVER_PORT;
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// HttpServletRequest properties // HttpServletRequest properties
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -180,7 +183,6 @@ public class MockHttpServletRequest implements HttpServletRequest {
/** /**
* Create a new MockHttpServletRequest with a default * Create a new MockHttpServletRequest with a default
* {@link MockServletContext}. * {@link MockServletContext}.
*
* @see MockServletContext * @see MockServletContext
*/ */
public MockHttpServletRequest() { public MockHttpServletRequest() {
@ -190,7 +192,6 @@ public class MockHttpServletRequest implements HttpServletRequest {
/** /**
* Create a new MockHttpServletRequest with a default * Create a new MockHttpServletRequest with a default
* {@link MockServletContext}. * {@link MockServletContext}.
*
* @param method the request method (may be <code>null</code>) * @param method the request method (may be <code>null</code>)
* @param requestURI the request URI (may be <code>null</code>) * @param requestURI the request URI (may be <code>null</code>)
* @see #setMethod * @see #setMethod
@ -203,7 +204,6 @@ public class MockHttpServletRequest implements HttpServletRequest {
/** /**
* Create a new MockHttpServletRequest. * Create a new MockHttpServletRequest.
*
* @param servletContext the ServletContext that the request runs in (may be * @param servletContext the ServletContext that the request runs in (may be
* <code>null</code> to use a default MockServletContext) * <code>null</code> to use a default MockServletContext)
* @see MockServletContext * @see MockServletContext
@ -214,7 +214,6 @@ public class MockHttpServletRequest implements HttpServletRequest {
/** /**
* Create a new MockHttpServletRequest. * Create a new MockHttpServletRequest.
*
* @param servletContext the ServletContext that the request runs in (may be * @param servletContext the ServletContext that the request runs in (may be
* <code>null</code> to use a default MockServletContext) * <code>null</code> to use a default MockServletContext)
* @param method the request method (may be <code>null</code>) * @param method the request method (may be <code>null</code>)
@ -230,6 +229,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
this.locales.add(Locale.ENGLISH); this.locales.add(Locale.ENGLISH);
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Lifecycle methods // Lifecycle methods
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -274,6 +274,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
} }
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// ServletRequest interface // ServletRequest interface
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -603,6 +604,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
return this.localPort; return this.localPort;
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// HttpServletRequest interface // HttpServletRequest interface
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -625,18 +627,15 @@ public class MockHttpServletRequest implements HttpServletRequest {
/** /**
* Add a header entry for the given name. * Add a header entry for the given name.
* <p> * <p>If there was no entry for that header name before, the value will be used
* If there was no entry for that header name before, the value will be used
* as-is. In case of an existing entry, a String array will be created, * as-is. In case of an existing entry, a String array will be created,
* adding the given value (more specifically, its toString representation) * adding the given value (more specifically, its toString representation)
* as further element. * as further element.
* <p> * <p>Multiple values can only be stored as list of Strings, following the
* Multiple values can only be stored as list of Strings, following the
* Servlet spec (see <code>getHeaders</code> accessor). As alternative to * Servlet spec (see <code>getHeaders</code> accessor). As alternative to
* repeated <code>addHeader</code> calls for individual elements, you can * repeated <code>addHeader</code> calls for individual elements, you can
* use a single call with an entire array or Collection of values as * use a single call with an entire array or Collection of values as
* parameter. * parameter.
*
* @see #getHeaderNames * @see #getHeaderNames
* @see #getHeader * @see #getHeader
* @see #getHeaders * @see #getHeaders
@ -680,20 +679,6 @@ public class MockHttpServletRequest implements HttpServletRequest {
} }
} }
public String getHeader(String name) {
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
return (header != null ? header.getValue().toString() : null);
}
public Enumeration<String> getHeaders(String name) {
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
return Collections.enumeration(header != null ? header.getStringValues() : new LinkedList<String>());
}
public Enumeration<String> getHeaderNames() {
return Collections.enumeration(this.headers.keySet());
}
public int getIntHeader(String name) { public int getIntHeader(String name) {
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
Object value = (header != null ? header.getValue() : null); Object value = (header != null ? header.getValue() : null);
@ -711,6 +696,20 @@ public class MockHttpServletRequest implements HttpServletRequest {
} }
} }
public String getHeader(String name) {
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
return (header != null ? header.getStringValue() : null);
}
public Enumeration<String> getHeaders(String name) {
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
return Collections.enumeration(header != null ? header.getStringValues() : new LinkedList<String>());
}
public Enumeration<String> getHeaderNames() {
return Collections.enumeration(this.headers.keySet());
}
public void setMethod(String method) { public void setMethod(String method) {
this.method = method; this.method = method;
} }
@ -760,7 +759,8 @@ public class MockHttpServletRequest implements HttpServletRequest {
} }
public boolean isUserInRole(String role) { public boolean isUserInRole(String role) {
return this.userRoles.contains(role); return (this.userRoles.contains(role) || (this.servletContext instanceof MockServletContext &&
((MockServletContext) this.servletContext).getDeclaredRoles().contains(role)));
} }
public void setUserPrincipal(Principal userPrincipal) { public void setUserPrincipal(Principal userPrincipal) {
@ -855,4 +855,18 @@ public class MockHttpServletRequest implements HttpServletRequest {
return isRequestedSessionIdFromURL(); return isRequestedSessionIdFromURL();
} }
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
return (this.userPrincipal != null && this.remoteUser != null && this.authType != null);
}
public void login(String username, String password) throws ServletException {
throw new ServletException("Username-password authentication not supported - override the login method");
}
public void logout() throws ServletException {
this.userPrincipal = null;
this.remoteUser = null;
this.authType = null;
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2011 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,11 +38,9 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
/** /**
* Mock implementation of the {@link javax.servlet.http.HttpServletResponse} * Mock implementation of the {@link javax.servlet.http.HttpServletResponse} interface.
* interface. Supports the Servlet 2.5 API level.
* *
* <p>Used for testing the web framework; also useful for testing * <p>Compatible with Servlet 2.5 as well as Servlet 3.0.
* application controllers.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Rod Johnson * @author Rod Johnson
@ -50,8 +48,6 @@ import org.springframework.web.util.WebUtils;
*/ */
public class MockHttpServletResponse implements HttpServletResponse { public class MockHttpServletResponse implements HttpServletResponse {
public static final int DEFAULT_SERVER_PORT = 80;
private static final String CHARSET_PREFIX = "charset="; private static final String CHARSET_PREFIX = "charset=";
@ -280,19 +276,52 @@ public class MockHttpServletResponse implements HttpServletResponse {
/** /**
* Return the names of all specified headers as a Set of Strings. * Return the names of all specified headers as a Set of Strings.
* <p>As of Servlet 3.0, this method is also defined HttpServletResponse.
* @return the <code>Set</code> of header name <code>Strings</code>, or an empty <code>Set</code> if none * @return the <code>Set</code> of header name <code>Strings</code>, or an empty <code>Set</code> if none
*/ */
public Set<String> getHeaderNames() { public Set<String> getHeaderNames() {
return this.headers.keySet(); return this.headers.keySet();
} }
/**
* Return the primary value for the given header as a String, if any.
* Will return the first value in case of multiple values.
* <p>As of Servlet 3.0, this method is also defined HttpServletResponse.
* As of Spring 3.1, it returns a stringified value for Servlet 3.0 compatibility.
* Consider using {@link #getHeaderValue(String)} for raw Object access.
* @param name the name of the header
* @return the associated header value, or <code>null<code> if none
*/
public String getHeader(String name) {
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
return (header != null ? header.getStringValue() : null);
}
/**
* Return all values for the given header as a List of Strings.
* <p>As of Servlet 3.0, this method is also defined HttpServletResponse.
* As of Spring 3.1, it returns a List of stringified values for Servlet 3.0 compatibility.
* Consider using {@link #getHeaderValues(String)} for raw Object access.
* @param name the name of the header
* @return the associated header values, or an empty List if none
*/
public List<String> getHeaders(String name) {
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
if (header != null) {
return header.getStringValues();
}
else {
return Collections.emptyList();
}
}
/** /**
* Return the primary value for the given header, if any. * Return the primary value for the given header, if any.
* <p>Will return the first value in case of multiple values. * <p>Will return the first value in case of multiple values.
* @param name the name of the header * @param name the name of the header
* @return the associated header value, or <code>null<code> if none * @return the associated header value, or <code>null<code> if none
*/ */
public Object getHeader(String name) { public Object getHeaderValue(String name) {
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
return (header != null ? header.getValue() : null); return (header != null ? header.getValue() : null);
} }
@ -302,9 +331,14 @@ public class MockHttpServletResponse implements HttpServletResponse {
* @param name the name of the header * @param name the name of the header
* @return the associated header values, or an empty List if none * @return the associated header values, or an empty List if none
*/ */
public List<Object> getHeaders(String name) { public List<Object> getHeaderValues(String name) {
HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name);
return (header != null ? header.getValues() : Collections.emptyList()); if (header != null) {
return header.getValues();
}
else {
return Collections.emptyList();
}
} }
/** /**

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2011 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,7 +23,6 @@ import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Vector; import java.util.Vector;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingEvent;
@ -34,10 +33,8 @@ import org.springframework.util.Assert;
/** /**
* Mock implementation of the {@link javax.servlet.http.HttpSession} interface. * Mock implementation of the {@link javax.servlet.http.HttpSession} interface.
* Supports the Servlet 2.4 API level. *
* <p> * <p>Compatible with Servlet 2.5 as well as Servlet 3.0.
* Used for testing the web framework; also useful for testing application
* controllers.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Rod Johnson * @author Rod Johnson
@ -47,8 +44,6 @@ import org.springframework.util.Assert;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class MockHttpSession implements HttpSession { public class MockHttpSession implements HttpSession {
public static final String SESSION_COOKIE_NAME = "JSESSION";
private static int nextId = 1; private static int nextId = 1;
private final String id; private final String id;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2011 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -42,12 +42,20 @@ import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
/** /**
* Mock implementation of the {@link javax.servlet.ServletContext} interface. * Mock implementation of the {@link javax.servlet.ServletContext} interface.
* *
* <p>Compatible with Servlet 2.5 and partially with Servlet 3.0. Can be configured to
* expose a specific version through {@link #setMajorVersion}/{@link #setMinorVersion};
* default is 2.5. Note that Servlet 3.0 support is limited: servlet, filter and listener
* registration methods are not supported; neither is cookie or JSP configuration.
* We generally do not recommend to unit-test your ServletContainerInitializers and
* WebApplicationInitializers which is where those registration methods would be used.
*
* <p>Used for testing the Spring web framework; only rarely necessary for testing * <p>Used for testing the Spring web framework; only rarely necessary for testing
* application controllers. As long as application components don't explicitly * application controllers. As long as application components don't explicitly
* access the ServletContext, ClassPathXmlApplicationContext or * access the ServletContext, ClassPathXmlApplicationContext or
@ -90,8 +98,14 @@ public class MockServletContext implements ServletContext {
private String contextPath = ""; private String contextPath = "";
private int majorVersion = 2;
private int minorVersion = 5; private int minorVersion = 5;
private int effectiveMajorVersion = 2;
private int effectiveMinorVersion = 5;
private final Map<String, ServletContext> contexts = new HashMap<String, ServletContext>(); private final Map<String, ServletContext> contexts = new HashMap<String, ServletContext>();
private final Map<String, String> initParameters = new LinkedHashMap<String, String>(); private final Map<String, String> initParameters = new LinkedHashMap<String, String>();
@ -100,6 +114,8 @@ public class MockServletContext implements ServletContext {
private String servletContextName = "MockServletContext"; private String servletContextName = "MockServletContext";
private final Set<String> declaredRoles = new HashSet<String>();
/** /**
* Create a new MockServletContext, using no base path and a * Create a new MockServletContext, using no base path and a
@ -179,14 +195,15 @@ public class MockServletContext implements ServletContext {
return this.contexts.get(contextPath); return this.contexts.get(contextPath);
} }
public void setMajorVersion(int majorVersion) {
this.majorVersion = majorVersion;
}
public int getMajorVersion() { public int getMajorVersion() {
return 2; return this.majorVersion;
} }
public void setMinorVersion(int minorVersion) { public void setMinorVersion(int minorVersion) {
if (minorVersion < 3 || minorVersion > 5) {
throw new IllegalArgumentException("Only Servlet minor versions between 3 and 5 are supported");
}
this.minorVersion = minorVersion; this.minorVersion = minorVersion;
} }
@ -194,6 +211,22 @@ public class MockServletContext implements ServletContext {
return this.minorVersion; return this.minorVersion;
} }
public void setEffectiveMajorVersion(int effectiveMajorVersion) {
this.effectiveMajorVersion = effectiveMajorVersion;
}
public int getEffectiveMajorVersion() {
return this.effectiveMajorVersion;
}
public void setEffectiveMinorVersion(int effectiveMinorVersion) {
this.effectiveMinorVersion = effectiveMinorVersion;
}
public int getEffectiveMinorVersion() {
return this.effectiveMinorVersion;
}
public String getMimeType(String filePath) { public String getMimeType(String filePath) {
return MimeTypeResolver.getMimeType(filePath); return MimeTypeResolver.getMimeType(filePath);
} }
@ -309,15 +342,24 @@ public class MockServletContext implements ServletContext {
return this.initParameters.get(name); return this.initParameters.get(name);
} }
public Enumeration<String> getInitParameterNames() {
return Collections.enumeration(this.initParameters.keySet());
}
public boolean setInitParameter(String name, String value) {
Assert.notNull(name, "Parameter name must not be null");
if (this.initParameters.containsKey(name)) {
return false;
}
this.initParameters.put(name, value);
return true;
}
public void addInitParameter(String name, String value) { public void addInitParameter(String name, String value) {
Assert.notNull(name, "Parameter name must not be null"); Assert.notNull(name, "Parameter name must not be null");
this.initParameters.put(name, value); this.initParameters.put(name, value);
} }
public Enumeration<String> getInitParameterNames() {
return Collections.enumeration(this.initParameters.keySet());
}
public Object getAttribute(String name) { public Object getAttribute(String name) {
Assert.notNull(name, "Attribute name must not be null"); Assert.notNull(name, "Attribute name must not be null");
return this.attributes.get(name); return this.attributes.get(name);
@ -350,6 +392,22 @@ public class MockServletContext implements ServletContext {
return this.servletContextName; return this.servletContextName;
} }
public ClassLoader getClassLoader() {
return ClassUtils.getDefaultClassLoader();
}
public void declareRoles(String... roleNames) {
Assert.notNull(roleNames, "Role names array must not be null");
for (String roleName : roleNames) {
Assert.hasLength(roleName, "Role name must not be empty");
this.declaredRoles.add(roleName);
}
}
public Set<String> getDeclaredRoles() {
return Collections.unmodifiableSet(this.declaredRoles);
}
/** /**
* Inner factory class used to just introduce a Java Activation Framework * Inner factory class used to just introduce a Java Activation Framework