Introduce support for HtmlUnit in Spring MVC Test
This commit introduces integration between MockMvc and HtmlUnit, thus simplifying end-to-end testing when using HTML-based views and enabling developers to do the following. - Easily test HTML pages using tools such as HtmlUnit, WebDriver, & Geb without the need to deploy to a Servlet container - Test JavaScript within pages - Optionally test using mock services to speed up testing - Share logic between in-container, end-to-end tests and out-of-container integration tests Issue: SPR-13158
This commit is contained in:
@@ -44,6 +44,7 @@ configure(allprojects) { project ->
|
||||
ext.httpclientVersion = "4.5"
|
||||
ext.httpasyncVersion = "4.1"
|
||||
ext.jackson2Version = "2.6.0"
|
||||
ext.htmlunitVersion = "2.17"
|
||||
ext.jasperreportsVersion = "6.1.0"
|
||||
ext.javamailVersion = "1.5.4"
|
||||
ext.jettyVersion = "9.3.1.v20150714"
|
||||
@@ -56,6 +57,7 @@ configure(allprojects) { project ->
|
||||
ext.poiVersion = "3.12"
|
||||
ext.protobufVersion = "2.6.1"
|
||||
ext.reactorVersion = "2.0.4.RELEASE"
|
||||
ext.seleniumVersion = "2.46.0"
|
||||
ext.slf4jVersion = "1.7.12"
|
||||
ext.snakeyamlVersion = "1.15"
|
||||
ext.snifferVersion = "1.14"
|
||||
@@ -1009,6 +1011,8 @@ project("spring-test") {
|
||||
optional("com.jayway.jsonpath:json-path:2.0.0")
|
||||
optional("org.skyscreamer:jsonassert:1.2.3")
|
||||
optional("xmlunit:xmlunit:${xmlunitVersion}")
|
||||
optional("net.sourceforge.htmlunit:htmlunit:$htmlunitVersion")
|
||||
optional("org.seleniumhq.selenium:selenium-htmlunit-driver:$seleniumVersion")
|
||||
testCompile(project(":spring-context-support"))
|
||||
testCompile(project(":spring-oxm"))
|
||||
testCompile("javax.mail:javax.mail-api:${javamailVersion}")
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebConnection;
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import com.gargoylesoftware.htmlunit.WebResponse;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Implementation of WebConnection that allows delegating to various WebConnection implementations. For example, if
|
||||
* you host your JavaScript on the domain code.jquery.com, you might want to use the following:</p>
|
||||
* <pre>
|
||||
* WebClient webClient = new WebClient();
|
||||
*
|
||||
* MockMvc mockMvc = ...
|
||||
* MockMvcWebConnection mockConnection = new MockMvcWebConnection(mockMvc);
|
||||
* mockConnection.setWebClient(webClient);
|
||||
*
|
||||
* WebRequestMatcher cdnMatcher = new UrlRegexRequestMatcher(".*?//code.jquery.com/.*");
|
||||
* WebConnection httpConnection = new HttpWebConnection(webClient);
|
||||
* WebConnection webConnection = new DelegatingWebConnection(mockConnection, new DelegateWebConnection(cdnMatcher, httpConnection));
|
||||
*
|
||||
* webClient.setWebConnection(webConnection);
|
||||
*
|
||||
* WebClient webClient = new WebClient();
|
||||
* webClient.setWebConnection(webConnection);
|
||||
* </pre>
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
public final class DelegatingWebConnection implements WebConnection {
|
||||
private final List<DelegateWebConnection> connections;
|
||||
private final WebConnection defaultConnection;
|
||||
|
||||
public DelegatingWebConnection(WebConnection defaultConnection, List<DelegateWebConnection> connections) {
|
||||
Assert.notNull(defaultConnection, "defaultConnection cannot be null");
|
||||
Assert.notEmpty(connections, "connections cannot be empty");
|
||||
this.connections = connections;
|
||||
this.defaultConnection = defaultConnection;
|
||||
}
|
||||
|
||||
public DelegatingWebConnection(WebConnection defaultConnection,DelegateWebConnection... connections) {
|
||||
this(defaultConnection, Arrays.asList(connections));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResponse getResponse(WebRequest request) throws IOException {
|
||||
for(DelegateWebConnection connection : connections) {
|
||||
if(connection.getMatcher().matches(request)) {
|
||||
return connection.getDelegate().getResponse(request);
|
||||
}
|
||||
}
|
||||
return defaultConnection.getResponse(request);
|
||||
}
|
||||
|
||||
public final static class DelegateWebConnection {
|
||||
private final WebRequestMatcher matcher;
|
||||
private final WebConnection delegate;
|
||||
|
||||
public DelegateWebConnection(WebRequestMatcher matcher, WebConnection delegate) {
|
||||
this.matcher = matcher;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
private WebRequestMatcher getMatcher() {
|
||||
return matcher;
|
||||
}
|
||||
|
||||
private WebConnection getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.test.web.servlet.request.RequestPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
final class ForwardRequestPostProcessor implements RequestPostProcessor {
|
||||
private final String forwardUrl;
|
||||
|
||||
public ForwardRequestPostProcessor(String url) {
|
||||
Assert.hasText(url, "Forward url must have text");
|
||||
forwardUrl = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
|
||||
request.setServletPath(forwardUrl);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An implementation of WebRequestMatcher that allows matching on the host and optionally
|
||||
* the port of WebRequest#getUrl(). For example, the following would match any request to
|
||||
* the host "code.jquery.com" without regard for the port:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* WebRequestMatcher cdnMatcher = new HostMatcher("code.jquery.com");
|
||||
* </pre>
|
||||
*
|
||||
* Multiple hosts can also be passed in. For example, the following would match an request
|
||||
* to the host "code.jquery.com" or the host "cdn.com" without regard for the port:
|
||||
*
|
||||
* <pre>
|
||||
* WebRequestMatcher cdnMatcher = new HostMatcher("code.jquery.com", "cdn.com");
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Alternatively, one can also specify the port. For example, the following would match
|
||||
* any request to the host "code.jquery.com" with the port of 80.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* WebRequestMatcher cdnMatcher = new HostMatcher("code.jquery.com:80");
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The above cdnMatcher would match: "http://code.jquery.com/jquery.js" (default port of
|
||||
* 80) and "http://code.jquery.com:80/jquery.js". However, it would not match
|
||||
* "https://code.jquery.com/jquery.js" (default port of 443).
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
* @see UrlRegexRequestMatcher
|
||||
* @see org.springframework.test.web.servlet.htmlunit.DelegatingWebConnection
|
||||
*/
|
||||
public final class HostRequestMatcher implements WebRequestMatcher {
|
||||
private final Set<String> hosts = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param hosts the hosts to match on (i.e. "localhost", "example.com:443")
|
||||
*/
|
||||
public HostRequestMatcher(String... hosts) {
|
||||
this.hosts.addAll(Arrays.asList(hosts));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(WebRequest request) {
|
||||
URL url = request.getUrl();
|
||||
String host = url.getHost();
|
||||
|
||||
if(hosts.contains(host)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int port = url.getPort();
|
||||
if(port == -1) {
|
||||
port = url.getDefaultPort();
|
||||
}
|
||||
String hostAndPort = host + ":" + port;
|
||||
|
||||
return hosts.contains(hostAndPort);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.CookieManager;
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import com.gargoylesoftware.htmlunit.util.NameValuePair;
|
||||
|
||||
import org.springframework.beans.Mergeable;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.test.web.servlet.RequestBuilder;
|
||||
import org.springframework.test.web.servlet.SmartRequestBuilder;
|
||||
import org.springframework.test.web.servlet.request.RequestPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Internal class used to allow a {@link WebRequest} into a {@link MockHttpServletRequest} using Spring MVC Test's
|
||||
* {@link RequestBuilder}.
|
||||
* </p>
|
||||
* <p>
|
||||
* By default the first path segment of the URL is used as the contextPath. To override this default see
|
||||
* {@link #setContextPath(String)}.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
* @see MockMvcWebConnection
|
||||
*/
|
||||
final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable {
|
||||
private final Map<String, MockHttpSession> sessions;
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
private final WebRequest webRequest;
|
||||
|
||||
private String contextPath;
|
||||
|
||||
private RequestBuilder parentBuilder;
|
||||
|
||||
private SmartRequestBuilder parentPostProcessor;
|
||||
|
||||
private RequestPostProcessor forwardPostProcessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sessions A {@link Map} of the {@link HttpSession#getId()} to currently managed {@link HttpSession}
|
||||
* objects. Cannot be null.
|
||||
* @param webClient the WebClient for retrieving cookies
|
||||
* @param webRequest The {@link WebRequest} to transform into a {@link MockHttpServletRequest}. Cannot be null.
|
||||
*/
|
||||
public HtmlUnitRequestBuilder(Map<String, MockHttpSession> sessions, WebClient webClient,
|
||||
WebRequest webRequest) {
|
||||
Assert.notNull(sessions, "sessions cannot be null");
|
||||
Assert.notNull(webClient, "webClient cannot be null");
|
||||
Assert.notNull(webRequest, "webRequest cannot be null");
|
||||
|
||||
this.sessions = sessions;
|
||||
this.webClient = webClient;
|
||||
this.webRequest = webRequest;
|
||||
}
|
||||
|
||||
public MockHttpServletRequest buildRequest(ServletContext servletContext) {
|
||||
String charset = getCharset();
|
||||
String httpMethod = webRequest.getHttpMethod().name();
|
||||
UriComponents uriComponents = uriComponents();
|
||||
|
||||
MockHttpServletRequest result = new HtmlUnitMockHttpServletRequest(servletContext, httpMethod,
|
||||
uriComponents.getPath());
|
||||
parent(result, parentBuilder);
|
||||
result.setServerName(uriComponents.getHost()); // needs to be first for additional headers
|
||||
authType(result);
|
||||
result.setCharacterEncoding(charset);
|
||||
content(result, charset);
|
||||
contextPath(result, uriComponents);
|
||||
contentType(result);
|
||||
cookies(result);
|
||||
headers(result);
|
||||
locales(result);
|
||||
servletPath(uriComponents, result);
|
||||
params(result, uriComponents);
|
||||
ports(uriComponents, result);
|
||||
result.setProtocol("HTTP/1.1");
|
||||
result.setQueryString(uriComponents.getQuery());
|
||||
result.setScheme(uriComponents.getScheme());
|
||||
pathInfo(uriComponents,result);
|
||||
|
||||
return postProcess(result);
|
||||
}
|
||||
|
||||
private MockHttpServletRequest postProcess(MockHttpServletRequest request) {
|
||||
if(parentPostProcessor != null) {
|
||||
request = parentPostProcessor.postProcessRequest(request);
|
||||
}
|
||||
if(forwardPostProcessor != null) {
|
||||
request = forwardPostProcessor.postProcessRequest(request);
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private void parent(MockHttpServletRequest result, RequestBuilder parent) {
|
||||
if(parent == null) {
|
||||
return;
|
||||
}
|
||||
MockHttpServletRequest parentRequest = parent.buildRequest(result.getServletContext());
|
||||
|
||||
// session
|
||||
HttpSession parentSession = parentRequest.getSession(false);
|
||||
if(parentSession != null) {
|
||||
Enumeration<String> attrNames = parentSession.getAttributeNames();
|
||||
while(attrNames.hasMoreElements()) {
|
||||
String attrName = attrNames.nextElement();
|
||||
Object attrValue = parentSession.getAttribute(attrName);
|
||||
result.getSession().setAttribute(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
|
||||
// header
|
||||
Enumeration<String> headerNames = parentRequest.getHeaderNames();
|
||||
while(headerNames.hasMoreElements()) {
|
||||
String attrName = headerNames.nextElement();
|
||||
Enumeration<String> attrValues = parentRequest.getHeaders(attrName);
|
||||
while(attrValues.hasMoreElements()) {
|
||||
String attrValue = attrValues.nextElement();
|
||||
result.addHeader(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
|
||||
// parameter
|
||||
Map<String, String[]> parentParams = parentRequest.getParameterMap();
|
||||
for(Map.Entry<String,String[]> parentParam : parentParams.entrySet()) {
|
||||
String paramName = parentParam.getKey();
|
||||
String[] paramValues = parentParam.getValue();
|
||||
result.addParameter(paramName, paramValues);
|
||||
}
|
||||
|
||||
// cookie
|
||||
Cookie[] parentCookies = parentRequest.getCookies();
|
||||
if(parentCookies != null) {
|
||||
result.setCookies(parentCookies);
|
||||
}
|
||||
|
||||
// request attribute
|
||||
Enumeration<String> parentAttrNames = parentRequest.getAttributeNames();
|
||||
while(parentAttrNames.hasMoreElements()) {
|
||||
String parentAttrName = parentAttrNames.nextElement();
|
||||
result.setAttribute(parentAttrName, parentRequest.getAttribute(parentAttrName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the contextPath to be used. The value may be null in which case the first path segment of the URL is turned
|
||||
* into the contextPath. Otherwise it must conform to {@link HttpServletRequest#getContextPath()} which states it
|
||||
* can be empty string or it must start with a "/" and not end in a "/".
|
||||
*
|
||||
* @param contextPath A valid contextPath
|
||||
* @throws IllegalArgumentException if contextPath is not a valid {@link HttpServletRequest#getContextPath()}.
|
||||
*/
|
||||
public void setContextPath(String contextPath) {
|
||||
if (contextPath == null || "".equals(contextPath)) {
|
||||
this.contextPath = contextPath;
|
||||
return;
|
||||
}
|
||||
if (contextPath.endsWith("/")) {
|
||||
throw new IllegalArgumentException("contextPath cannot end with /. Got '" + contextPath + "'");
|
||||
}
|
||||
if (!contextPath.startsWith("/")) {
|
||||
throw new IllegalArgumentException("contextPath must start with /. Got '" + contextPath + "'");
|
||||
}
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
public void setForwardPostProcessor(RequestPostProcessor postProcessor) {
|
||||
this.forwardPostProcessor = postProcessor;
|
||||
}
|
||||
|
||||
private void authType(MockHttpServletRequest request) {
|
||||
String authorization = header("Authorization");
|
||||
if (authorization != null) {
|
||||
String[] authzParts = authorization.split(": ");
|
||||
request.setAuthType(authzParts[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void content(MockHttpServletRequest result, String charset) {
|
||||
String requestBody = webRequest.getRequestBody();
|
||||
if (requestBody == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
result.setContent(requestBody.getBytes(charset));
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void contentType(MockHttpServletRequest result) {
|
||||
String contentType = header("Content-Type");
|
||||
result.setContentType(contentType == null ? MediaType.ALL_VALUE.toString() : contentType);
|
||||
}
|
||||
|
||||
private void contextPath(MockHttpServletRequest result, UriComponents uriComponents) {
|
||||
if (contextPath == null) {
|
||||
List<String> pathSegments = uriComponents.getPathSegments();
|
||||
if (pathSegments.isEmpty()) {
|
||||
result.setContextPath("");
|
||||
}
|
||||
else {
|
||||
result.setContextPath("/" + pathSegments.get(0));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!uriComponents.getPath().startsWith(contextPath)) {
|
||||
throw new IllegalArgumentException(uriComponents.getPath() + " should start with contextPath "
|
||||
+ contextPath);
|
||||
}
|
||||
result.setContextPath(contextPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void cookies(MockHttpServletRequest result) {
|
||||
String cookieHeaderValue = header("Cookie");
|
||||
Cookie[] parentCookies = result.getCookies();
|
||||
List<Cookie> cookies = new ArrayList<Cookie>();
|
||||
if (cookieHeaderValue != null) {
|
||||
StringTokenizer tokens = new StringTokenizer(cookieHeaderValue, "=;");
|
||||
while (tokens.hasMoreTokens()) {
|
||||
String cookieName = tokens.nextToken().trim();
|
||||
if (!tokens.hasMoreTokens()) {
|
||||
throw new IllegalArgumentException("Expected value for cookie name " + cookieName
|
||||
+ ". Full cookie was " + cookieHeaderValue);
|
||||
}
|
||||
String cookieValue = tokens.nextToken().trim();
|
||||
processCookie(result, cookies, new Cookie(cookieName, cookieValue));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<com.gargoylesoftware.htmlunit.util.Cookie> managedCookies = webClient.getCookies(webRequest.getUrl());
|
||||
for (com.gargoylesoftware.htmlunit.util.Cookie cookie : managedCookies) {
|
||||
processCookie(result, cookies, new Cookie(cookie.getName(), cookie.getValue()));
|
||||
}
|
||||
if(parentCookies != null) {
|
||||
for(Cookie cookie : parentCookies) {
|
||||
cookies.add(cookie);
|
||||
}
|
||||
}
|
||||
if (!cookies.isEmpty()) {
|
||||
result.setCookies(cookies.toArray(new Cookie[0]));
|
||||
}
|
||||
}
|
||||
|
||||
private void processCookie(MockHttpServletRequest result, List<Cookie> cookies, Cookie cookie) {
|
||||
cookies.add(cookie);
|
||||
if ("JSESSIONID".equals(cookie.getName())) {
|
||||
result.setRequestedSessionId(cookie.getValue());
|
||||
result.setSession(httpSession(result, cookie.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private String getCharset() {
|
||||
String charset = webRequest.getCharset();
|
||||
if (charset == null) {
|
||||
return "ISO-8859-1";
|
||||
}
|
||||
return charset;
|
||||
}
|
||||
|
||||
private String header(String headerName) {
|
||||
return webRequest.getAdditionalHeaders().get(headerName);
|
||||
}
|
||||
|
||||
private void headers(MockHttpServletRequest result) {
|
||||
for (Entry<String, String> header : webRequest.getAdditionalHeaders().entrySet()) {
|
||||
result.addHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private MockHttpSession httpSession(MockHttpServletRequest request, final String sessionid) {
|
||||
MockHttpSession session;
|
||||
synchronized (sessions) {
|
||||
session = sessions.get(sessionid);
|
||||
if (session == null) {
|
||||
session = new HtmlUnitMockHttpSession(request, sessionid);
|
||||
session.setNew(true);
|
||||
synchronized (sessions) {
|
||||
sessions.put(sessionid, session);
|
||||
}
|
||||
addSessionCookie(request, sessionid);
|
||||
}
|
||||
else {
|
||||
session.setNew(false);
|
||||
}
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
private void addSessionCookie(MockHttpServletRequest request, String sessionid) {
|
||||
getCookieManager().addCookie(createCookie(request, sessionid));
|
||||
}
|
||||
|
||||
private void removeSessionCookie(MockHttpServletRequest request, String sessionid) {
|
||||
getCookieManager().removeCookie(createCookie(request, sessionid));
|
||||
}
|
||||
|
||||
private com.gargoylesoftware.htmlunit.util.Cookie createCookie(MockHttpServletRequest request, String sessionid) {
|
||||
return new com.gargoylesoftware.htmlunit.util.Cookie(request.getServerName(), "JSESSIONID", sessionid,
|
||||
request.getContextPath() + "/", null, request.isSecure(), true);
|
||||
}
|
||||
|
||||
private void locales(MockHttpServletRequest result) {
|
||||
String locale = header("Accept-Language");
|
||||
if (locale == null) {
|
||||
result.addPreferredLocale(Locale.getDefault());
|
||||
}
|
||||
else {
|
||||
String[] locales = locale.split(", ");
|
||||
for (int i = locales.length - 1; i >= 0; i--) {
|
||||
result.addPreferredLocale(parseLocale(locales[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void params(MockHttpServletRequest result, UriComponents uriComponents) {
|
||||
for (Entry<String, List<String>> values : uriComponents.getQueryParams().entrySet()) {
|
||||
String name = values.getKey();
|
||||
for (String value : values.getValue()) {
|
||||
try {
|
||||
result.addParameter(name, URLDecoder.decode(value, "UTF-8"));
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (NameValuePair param : webRequest.getRequestParameters()) {
|
||||
result.addParameter(param.getName(), param.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private Locale parseLocale(String locale) {
|
||||
Matcher matcher = LOCALE_PATTERN.matcher(locale);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException("Invalid locale " + locale);
|
||||
}
|
||||
String language = matcher.group(1);
|
||||
String country = matcher.group(2);
|
||||
if (country == null) {
|
||||
country = "";
|
||||
}
|
||||
String qualifier = matcher.group(3);
|
||||
if (qualifier == null) {
|
||||
qualifier = "";
|
||||
}
|
||||
return new Locale(language, country, qualifier);
|
||||
}
|
||||
|
||||
private void pathInfo(UriComponents uriComponents, MockHttpServletRequest result) {
|
||||
result.setPathInfo(null);
|
||||
}
|
||||
|
||||
private void servletPath(MockHttpServletRequest result, String requestPath) {
|
||||
String servletPath = requestPath.substring(result.getContextPath().length());
|
||||
if ("".equals(servletPath)) {
|
||||
servletPath = null;
|
||||
}
|
||||
result.setServletPath(servletPath);
|
||||
}
|
||||
|
||||
private void servletPath(UriComponents uriComponents, MockHttpServletRequest result) {
|
||||
if ("".equals(result.getPathInfo())) {
|
||||
result.setPathInfo(null);
|
||||
}
|
||||
servletPath(result, uriComponents.getPath());
|
||||
}
|
||||
|
||||
private void ports(UriComponents uriComponents, MockHttpServletRequest result) {
|
||||
int serverPort = uriComponents.getPort();
|
||||
result.setServerPort(serverPort);
|
||||
if (serverPort == -1) {
|
||||
int portConnection = webRequest.getUrl().getDefaultPort();
|
||||
result.setLocalPort(serverPort);
|
||||
result.setRemotePort(portConnection);
|
||||
}
|
||||
else {
|
||||
result.setRemotePort(serverPort);
|
||||
}
|
||||
}
|
||||
|
||||
private UriComponents uriComponents() {
|
||||
URL url = webRequest.getUrl();
|
||||
UriComponentsBuilder uriBldr = UriComponentsBuilder.fromUriString(url.toExternalForm());
|
||||
return uriBldr.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMergeEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object merge(Object parent) {
|
||||
if (parent == null) {
|
||||
return this;
|
||||
}
|
||||
if(parent instanceof RequestBuilder) {
|
||||
this.parentBuilder = (RequestBuilder) parent;
|
||||
}
|
||||
if (parent instanceof SmartRequestBuilder) {
|
||||
this.parentPostProcessor = (SmartRequestBuilder) parent;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension to {@link MockHttpServletRequest} that ensures that when a new {@link HttpSession} is created, it is
|
||||
* added to the managed sessions.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
private final class HtmlUnitMockHttpServletRequest extends MockHttpServletRequest {
|
||||
private HtmlUnitMockHttpServletRequest(ServletContext servletContext, String method, String requestURI) {
|
||||
super(servletContext, method, requestURI);
|
||||
}
|
||||
|
||||
public HttpSession getSession(boolean create) {
|
||||
HttpSession result = super.getSession(false);
|
||||
if (result == null && create) {
|
||||
HtmlUnitMockHttpSession newSession = new HtmlUnitMockHttpSession(this);
|
||||
setSession(newSession);
|
||||
newSession.setNew(true);
|
||||
String sessionid = newSession.getId();
|
||||
synchronized (sessions) {
|
||||
sessions.put(sessionid, newSession);
|
||||
}
|
||||
addSessionCookie(this, sessionid);
|
||||
result = newSession;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public HttpSession getSession() {
|
||||
return super.getSession();
|
||||
}
|
||||
|
||||
public void setSession(HttpSession session) {
|
||||
super.setSession(session);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension to {@link MockHttpSession} that ensures when {@link #invalidate()} is called that the
|
||||
* {@link HttpSession} is removed from the managed sessions.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
private final class HtmlUnitMockHttpSession extends MockHttpSession {
|
||||
private final MockHttpServletRequest request;
|
||||
|
||||
private HtmlUnitMockHttpSession(MockHttpServletRequest request) {
|
||||
super(request.getServletContext());
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
private HtmlUnitMockHttpSession(MockHttpServletRequest request, String id) {
|
||||
super(request.getServletContext(), id);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
super.invalidate();
|
||||
synchronized (sessions) {
|
||||
sessions.remove(getId());
|
||||
}
|
||||
removeSessionCookie(request, getId());
|
||||
}
|
||||
}
|
||||
|
||||
private CookieManager getCookieManager() {
|
||||
return webClient.getCookieManager();
|
||||
}
|
||||
|
||||
private static final Pattern LOCALE_PATTERN = Pattern.compile("^\\s*(\\w{2})(?:-(\\w{2}))?(?:;q=(\\d+\\.\\d+))?$");
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Simplifies creating a WebClient that delegates to a MockMvc instance.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
public class MockMvcWebClientBuilder extends MockMvcWebConnectionBuilderSupport<MockMvcWebClientBuilder> {
|
||||
|
||||
protected MockMvcWebClientBuilder(MockMvc mockMvc) {
|
||||
super(mockMvc);
|
||||
}
|
||||
|
||||
protected MockMvcWebClientBuilder(WebApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
protected MockMvcWebClientBuilder(WebApplicationContext context, MockMvcConfigurer configurer) {
|
||||
super(context, configurer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with a WebApplicationContext.
|
||||
*
|
||||
* @param context the WebApplicationContext to use. Cannot be null.
|
||||
* @return the MockMvcWebClientBuilder to customize
|
||||
*/
|
||||
public static MockMvcWebClientBuilder webAppContextSetup(WebApplicationContext context) {
|
||||
return new MockMvcWebClientBuilder(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance using a WebApplicationContext
|
||||
* @param context the WebApplicationContext to create a MockMvc instance from.
|
||||
* @param configurer the MockMvcConfigurer to apply
|
||||
* Cannot be null.
|
||||
* @return the MockMvcWebClientBuilder to use
|
||||
*/
|
||||
public static MockMvcWebClientBuilder webAppContextSetup(WebApplicationContext context, MockMvcConfigurer configurer) {
|
||||
return new MockMvcWebClientBuilder(context, configurer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with a MockMvc instance.
|
||||
*
|
||||
* @param mockMvc the MockMvc to use. Cannot be null.
|
||||
* @return the MockMvcWebClientBuilder to customize
|
||||
*/
|
||||
public static MockMvcWebClientBuilder mockMvcSetup(MockMvc mockMvc) {
|
||||
return new MockMvcWebClientBuilder(mockMvc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WebClient that uses the provided MockMvc for any matching requests and a
|
||||
* WebClient with all the default settings for any other request.
|
||||
*
|
||||
* @return the WebClient to use
|
||||
*/
|
||||
public WebClient createWebClient() {
|
||||
return configureWebClient(new WebClient());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WebClient that uses the provided MockMvc for any matching requests and the
|
||||
* provided WebClient for any other request.
|
||||
*
|
||||
* @param webClient The WebClient to delegate to for requests that do not match. Cannot be null.
|
||||
*
|
||||
* @return the WebClient to use
|
||||
*/
|
||||
public WebClient configureWebClient(WebClient webClient) {
|
||||
webClient.setWebConnection(createConnection(webClient.getWebConnection()));
|
||||
return webClient;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.CookieManager;
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import com.gargoylesoftware.htmlunit.WebConnection;
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import com.gargoylesoftware.htmlunit.WebResponse;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.RequestBuilder;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
import org.springframework.test.web.servlet.htmlunit.webdriver.WebConnectionHtmlUnitDriver;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Allows {@link MockMvc} to transform a {@link WebRequest} into a {@link WebResponse}. This is the core integration
|
||||
* with <a href="http://htmlunit.sourceforge.net/">HTML Unit</a>.
|
||||
* </p>
|
||||
* <p>
|
||||
* Example usage can be seen below:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* WebClient webClient = new WebClient();
|
||||
* MockMvc mockMvc = ...
|
||||
* MockMvcWebConnection webConnection = new MockMvcWebConnection(mockMvc);
|
||||
* mockConnection.setWebClient(webClient);
|
||||
* webClient.setWebConnection(webConnection);
|
||||
*
|
||||
* ... use webClient as normal ...
|
||||
* </pre>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
* @see WebConnectionHtmlUnitDriver
|
||||
*/
|
||||
public final class MockMvcWebConnection implements WebConnection {
|
||||
private WebClient webClient;
|
||||
|
||||
private final Map<String, MockHttpSession> sessions = new HashMap<String, MockHttpSession>();
|
||||
|
||||
private final MockMvc mockMvc;
|
||||
|
||||
private final String contextPath;
|
||||
|
||||
/**
|
||||
* Creates a new instance that assumes the context root of the application is "". For example,
|
||||
* the URL http://localhost/test/this would use "" as the context root.
|
||||
*
|
||||
* @param mockMvc the MockMvc instance to use
|
||||
*/
|
||||
public MockMvcWebConnection(MockMvc mockMvc) {
|
||||
this(mockMvc, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with a specified context root.
|
||||
*
|
||||
* @param mockMvc the MockMvc instance to use
|
||||
* @param contextPath the contextPath to use. The value may be null in which case the first path segment of the URL is turned
|
||||
* into the contextPath. Otherwise it must conform to {@link HttpServletRequest#getContextPath()} which states it
|
||||
* can be empty string or it must start with a "/" and not end in a "/".
|
||||
*/
|
||||
public MockMvcWebConnection(MockMvc mockMvc, String contextPath) {
|
||||
Assert.notNull(mockMvc, "mockMvc cannot be null");
|
||||
validateContextPath(contextPath);
|
||||
|
||||
this.webClient = new WebClient();
|
||||
this.mockMvc = mockMvc;
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
public WebResponse getResponse(WebRequest webRequest) throws IOException {
|
||||
long startTime = System.currentTimeMillis();
|
||||
HtmlUnitRequestBuilder requestBuilder = new HtmlUnitRequestBuilder(sessions, webClient, webRequest);
|
||||
requestBuilder.setContextPath(contextPath);
|
||||
|
||||
MockHttpServletResponse httpServletResponse = getResponse(requestBuilder);
|
||||
|
||||
String forwardedUrl = httpServletResponse.getForwardedUrl();
|
||||
while(forwardedUrl != null) {
|
||||
requestBuilder.setForwardPostProcessor(new ForwardRequestPostProcessor(forwardedUrl));
|
||||
httpServletResponse = getResponse(requestBuilder);
|
||||
forwardedUrl = httpServletResponse.getForwardedUrl();
|
||||
}
|
||||
|
||||
return new MockWebResponseBuilder(startTime, webRequest, httpServletResponse).build();
|
||||
}
|
||||
|
||||
public void setWebClient(WebClient webClient) {
|
||||
Assert.notNull(webClient, "webClient cannot be null");
|
||||
this.webClient = webClient;
|
||||
}
|
||||
|
||||
private CookieManager getCookieManager() {
|
||||
return webClient.getCookieManager();
|
||||
}
|
||||
|
||||
private MockHttpServletResponse getResponse(RequestBuilder requestBuilder) throws IOException {
|
||||
ResultActions resultActions;
|
||||
try {
|
||||
resultActions = mockMvc.perform(requestBuilder);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw (IOException) new IOException(e.getMessage()).initCause(e);
|
||||
}
|
||||
|
||||
return resultActions.andReturn().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs validation on the contextPath
|
||||
*
|
||||
* @param contextPath the contextPath to validate
|
||||
*/
|
||||
private static void validateContextPath(String contextPath) {
|
||||
if (contextPath == null || "".equals(contextPath)) {
|
||||
return;
|
||||
}
|
||||
if (contextPath.endsWith("/")) {
|
||||
throw new IllegalArgumentException("contextPath cannot end with /. Got '" + contextPath + "'");
|
||||
}
|
||||
if (!contextPath.startsWith("/")) {
|
||||
throw new IllegalArgumentException("contextPath must start with /. Got '" + contextPath + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebConnection;
|
||||
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Makes it easy to create a WebConnection that uses MockMvc and optionally delegates to
|
||||
* a real WebConnection for specific requests. The default is to use MockMvc for any host
|
||||
* that is "localhost" and otherwise use a real WebConnection.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
public abstract class MockMvcWebConnectionBuilderSupport<T extends MockMvcWebConnectionBuilderSupport<T>> {
|
||||
private String contextPath = "";
|
||||
|
||||
private final MockMvc mockMvc;
|
||||
|
||||
private List<WebRequestMatcher> mockMvcRequestMatchers = new ArrayList<WebRequestMatcher>();
|
||||
|
||||
private boolean alwaysUseMockMvc;
|
||||
|
||||
/**
|
||||
* Creates a new instance using a MockMvc instance
|
||||
*
|
||||
* @param mockMvc the MockMvc instance to use. Cannot be null.
|
||||
*/
|
||||
protected MockMvcWebConnectionBuilderSupport(MockMvc mockMvc) {
|
||||
Assert.notNull(mockMvc, "mockMvc cannot be null");
|
||||
this.mockMvc = mockMvc;
|
||||
this.mockMvcRequestMatchers.add(new HostRequestMatcher("localhost"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance using a WebApplicationContext
|
||||
* @param context the WebApplicationContext to create a MockMvc instance from.
|
||||
* Cannot be null.
|
||||
*/
|
||||
protected MockMvcWebConnectionBuilderSupport(WebApplicationContext context) {
|
||||
this(MockMvcBuilders.webAppContextSetup(context).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance using a WebApplicationContext
|
||||
* @param context the WebApplicationContext to create a MockMvc instance from.
|
||||
* @param configurer the MockMvcConfigurer to apply
|
||||
* Cannot be null.
|
||||
*/
|
||||
protected MockMvcWebConnectionBuilderSupport(WebApplicationContext context, MockMvcConfigurer configurer) {
|
||||
this(MockMvcBuilders.webAppContextSetup(context).apply(configurer).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* The context path to use. Default is "". If the value is null, then the first path
|
||||
* segment of the request URL is assumed to be the context path.
|
||||
*
|
||||
* @param contextPath the context path to use.
|
||||
* @return the builder for further customization
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T contextPath(String contextPath) {
|
||||
this.contextPath = contextPath;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always use MockMvc no matter what the request looks like.
|
||||
*
|
||||
* @return the builder for further customization
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T alwaysUseMockMvc() {
|
||||
this.alwaysUseMockMvc = true;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional WebRequestMatcher instances that if return true will ensure MockMvc
|
||||
* is used.
|
||||
*
|
||||
* @param matchers the WebRequestMatcher instances that if true will ensure MockMvc
|
||||
* processes the request.
|
||||
* @return the builder for further customization
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T useMockMvc(WebRequestMatcher... matchers) {
|
||||
for(WebRequestMatcher matcher : matchers) {
|
||||
this.mockMvcRequestMatchers.add(matcher);
|
||||
}
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional WebRequestMatcher instances that will return true if the host matches.
|
||||
*
|
||||
* @param hosts the additional hosts that will ensure MockMvc gets invoked (i.e. example.com or example.com:8080).
|
||||
* @return the builder for further customization
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T useMockMvcForHosts(String... hosts) {
|
||||
this.mockMvcRequestMatchers.add(new HostRequestMatcher(hosts));
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WebConnection that will use a MockMvc instance if one of the
|
||||
* specified WebRequestMatcher matches.
|
||||
*
|
||||
* @param defaultConnection the default WebConnection to use if none of the specified
|
||||
* WebRequestMatcher instances match. Cannot be null.
|
||||
* @return a new WebConnection that will use a MockMvc instance if one of the
|
||||
* specified WebRequestMatcher matches.
|
||||
*
|
||||
* @see #alwaysUseMockMvc
|
||||
* @see #useMockMvc(WebRequestMatcher...)
|
||||
* @see #useMockMvcForHosts(String...)
|
||||
*/
|
||||
protected final WebConnection createConnection(WebConnection defaultConnection) {
|
||||
Assert.notNull(defaultConnection, "defaultConnection cannot be null");
|
||||
MockMvcWebConnection mockMvcWebConnection = new MockMvcWebConnection(mockMvc, contextPath);
|
||||
|
||||
if(alwaysUseMockMvc) {
|
||||
return mockMvcWebConnection;
|
||||
}
|
||||
|
||||
List<DelegatingWebConnection.DelegateWebConnection> delegates = new ArrayList<DelegatingWebConnection.DelegateWebConnection>(mockMvcRequestMatchers.size());
|
||||
for(WebRequestMatcher matcher : mockMvcRequestMatchers) {
|
||||
delegates.add(new DelegatingWebConnection.DelegateWebConnection(matcher, mockMvcWebConnection));
|
||||
}
|
||||
|
||||
return new DelegatingWebConnection(defaultConnection, delegates);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import com.gargoylesoftware.htmlunit.WebResponse;
|
||||
import com.gargoylesoftware.htmlunit.WebResponseData;
|
||||
import com.gargoylesoftware.htmlunit.util.NameValuePair;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
final class MockWebResponseBuilder {
|
||||
private final long startTime;
|
||||
|
||||
private final WebRequest webRequest;
|
||||
|
||||
private final MockHttpServletResponse response;
|
||||
|
||||
public MockWebResponseBuilder(long startTime, WebRequest webRequest, MockHttpServletResponse httpServletResponse) {
|
||||
Assert.notNull(webRequest, "webRequest");
|
||||
Assert.notNull(httpServletResponse, "httpServletResponse cannot be null");
|
||||
this.startTime = startTime;
|
||||
this.webRequest = webRequest;
|
||||
this.response = httpServletResponse;
|
||||
}
|
||||
|
||||
public WebResponse build() throws IOException {
|
||||
WebResponseData webResponseData = webResponseData();
|
||||
long endTime = System.currentTimeMillis();
|
||||
return new WebResponse(webResponseData, webRequest, endTime - startTime);
|
||||
}
|
||||
|
||||
private WebResponseData webResponseData() throws IOException {
|
||||
List<NameValuePair> responseHeaders = responseHeaders();
|
||||
int statusCode = response.getRedirectedUrl() == null ? response.getStatus() : 301;
|
||||
String statusMessage = statusMessage(statusCode);
|
||||
return new WebResponseData(response.getContentAsByteArray(), statusCode, statusMessage, responseHeaders);
|
||||
}
|
||||
|
||||
private String statusMessage(int statusCode) {
|
||||
String errorMessage = response.getErrorMessage();
|
||||
if (errorMessage != null) {
|
||||
return errorMessage;
|
||||
}
|
||||
try {
|
||||
return HttpStatus.valueOf(statusCode).getReasonPhrase();
|
||||
}
|
||||
catch (IllegalArgumentException useDefault) {
|
||||
}
|
||||
;
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
private List<NameValuePair> responseHeaders() {
|
||||
Collection<String> headerNames = response.getHeaderNames();
|
||||
List<NameValuePair> responseHeaders = new ArrayList<NameValuePair>(headerNames.size());
|
||||
for (String headerName : headerNames) {
|
||||
List<Object> headerValues = response.getHeaderValues(headerName);
|
||||
for (Object value : headerValues) {
|
||||
responseHeaders.add(new NameValuePair(headerName, String.valueOf(value)));
|
||||
}
|
||||
}
|
||||
String location = response.getRedirectedUrl();
|
||||
if (location != null) {
|
||||
responseHeaders.add(new NameValuePair("Location", location));
|
||||
}
|
||||
return responseHeaders;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An implementation of WebRequestMatcher that allows matching on WebRequest#getUrl().toExternalForm() using a regular expression. For example, if you would like to match on the domain code.jquery.com, you might want to use the following:</p>
|
||||
*
|
||||
* <pre>
|
||||
* WebRequestMatcher cdnMatcher = new UrlRegexRequestMatcher(".*?//code.jquery.com/.*");
|
||||
* </pre>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
* @see org.springframework.test.web.servlet.htmlunit.DelegatingWebConnection
|
||||
*/
|
||||
public final class UrlRegexRequestMatcher implements WebRequestMatcher {
|
||||
private Pattern pattern;
|
||||
|
||||
public UrlRegexRequestMatcher(String regex) {
|
||||
pattern = Pattern.compile(regex);
|
||||
}
|
||||
|
||||
public UrlRegexRequestMatcher(Pattern pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(WebRequest request) {
|
||||
String url = request.getUrl().toExternalForm();
|
||||
return pattern.matcher(url).matches();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
|
||||
/**
|
||||
* Strategy to match on a WebRequest
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
public interface WebRequestMatcher {
|
||||
/**
|
||||
* Return true if matches on WebRequest, else false
|
||||
*
|
||||
* @param request the WebRequest to attempt to match on
|
||||
* @return true if matches on WebRequest, else false
|
||||
*/
|
||||
boolean matches(WebRequest request);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Support for MockMvc and HtmlUnit integration
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
package org.springframework.test.web.servlet.htmlunit;
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit.webdriver;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.BrowserVersion;
|
||||
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
|
||||
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.htmlunit.MockMvcWebConnectionBuilderSupport;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Convenience class for building an HtmlUnitDriver that will delegate to MockMvc and
|
||||
* optionally delegate to an actual connection for specific requests.
|
||||
*
|
||||
* By default localhost will delegate to MockMvc and any other URL will delegate
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
public class MockMvcHtmlUnitDriverBuilder extends MockMvcWebConnectionBuilderSupport<MockMvcHtmlUnitDriverBuilder> {
|
||||
private boolean javascriptEnabled = true;
|
||||
|
||||
protected MockMvcHtmlUnitDriverBuilder(MockMvc mockMvc) {
|
||||
super(mockMvc);
|
||||
}
|
||||
|
||||
protected MockMvcHtmlUnitDriverBuilder(WebApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
protected MockMvcHtmlUnitDriverBuilder(WebApplicationContext context, MockMvcConfigurer configurer) {
|
||||
super(context, configurer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with a WebApplicationContext.
|
||||
*
|
||||
* @param context the WebApplicationContext to use. Cannot be null.
|
||||
* @return the MockMvcHtmlUnitDriverBuilder to customize
|
||||
*/
|
||||
public static MockMvcHtmlUnitDriverBuilder webAppContextSetup(WebApplicationContext context) {
|
||||
return new MockMvcHtmlUnitDriverBuilder(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance using a WebApplicationContext
|
||||
* @param context the WebApplicationContext to create a MockMvc instance from.
|
||||
* @param configurer the MockMvcConfigurer to apply
|
||||
* Cannot be null.
|
||||
* @return the MockMvcHtmlUnitDriverBuilder to customize
|
||||
*/
|
||||
public static MockMvcHtmlUnitDriverBuilder webAppContextSetup(WebApplicationContext context, MockMvcConfigurer configurer) {
|
||||
return new MockMvcHtmlUnitDriverBuilder(context, configurer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with a MockMvc instance.
|
||||
*
|
||||
* @param mockMvc the MockMvc to use. Cannot be null.
|
||||
* @return the MockMvcHtmlUnitDriverBuilder to customize
|
||||
*/
|
||||
public static MockMvcHtmlUnitDriverBuilder mockMvcSetup(MockMvc mockMvc) {
|
||||
return new MockMvcHtmlUnitDriverBuilder(mockMvc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies if JavaScript should be enabled or not. Default is true.
|
||||
*
|
||||
* @param javascriptEnabled if JavaScript should be enabled or not.
|
||||
* @return the builder for further customizations
|
||||
*/
|
||||
public MockMvcHtmlUnitDriverBuilder javascriptEnabled(boolean javascriptEnabled) {
|
||||
this.javascriptEnabled = javascriptEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new HtmlUnitDriver with the BrowserVersion set to CHROME. For additional
|
||||
* configuration options, use configureDriver.
|
||||
*
|
||||
* @return the HtmlUnitDriver to use
|
||||
* @see #configureDriver(WebConnectionHtmlUnitDriver)
|
||||
*/
|
||||
public HtmlUnitDriver createDriver() {
|
||||
return configureDriver(new WebConnectionHtmlUnitDriver(BrowserVersion.CHROME));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an existing WebConnectionHtmlUnitDriver.
|
||||
*
|
||||
* @param driver the WebConnectionHtmlUnitDriver to configure
|
||||
* @return the HtmlUnitDriver to use
|
||||
*/
|
||||
public HtmlUnitDriver configureDriver(WebConnectionHtmlUnitDriver driver) {
|
||||
driver.setJavascriptEnabled(javascriptEnabled);
|
||||
driver.setWebConnection(createConnection(driver.getWebConnection()));
|
||||
return driver;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit.webdriver;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.BrowserVersion;
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import com.gargoylesoftware.htmlunit.WebConnection;
|
||||
import org.openqa.selenium.Capabilities;
|
||||
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Allows configuring the WebConnection for an HtmlUnitDriver instance. This is useful
|
||||
* because it allows a MockMvcWebConnection to be injected.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
* @see MockMvcHtmlUnitDriverBuilder
|
||||
*/
|
||||
public class WebConnectionHtmlUnitDriver extends HtmlUnitDriver {
|
||||
private WebClient webClient;
|
||||
|
||||
public WebConnectionHtmlUnitDriver(BrowserVersion version) {
|
||||
super(version);
|
||||
}
|
||||
|
||||
public WebConnectionHtmlUnitDriver() {
|
||||
}
|
||||
|
||||
public WebConnectionHtmlUnitDriver(boolean enableJavascript) {
|
||||
super(enableJavascript);
|
||||
}
|
||||
|
||||
public WebConnectionHtmlUnitDriver(Capabilities capabilities) {
|
||||
super(capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the WebClient that is used so that its WebConnection is accessible.
|
||||
*
|
||||
* @param client The client to modify
|
||||
* @return The modified client
|
||||
*/
|
||||
@Override
|
||||
protected final WebClient modifyWebClient(WebClient client) {
|
||||
webClient = super.modifyWebClient(client);
|
||||
webClient = configureWebClient(webClient);
|
||||
return webClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to customise the WebClient that the HtmlUnit
|
||||
* driver uses.
|
||||
*
|
||||
* @param client The client to modify
|
||||
* @return The modified client
|
||||
*/
|
||||
protected WebClient configureWebClient(WebClient client) {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows accessing the current WebConnection
|
||||
*
|
||||
* @return the current WebConnection
|
||||
*/
|
||||
public WebConnection getWebConnection() {
|
||||
return webClient.getWebConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the WebConnection to be used.
|
||||
*
|
||||
* @param webConnection the WebConnection to use. Cannot be null.
|
||||
*/
|
||||
public void setWebConnection(WebConnection webConnection) {
|
||||
Assert.notNull(webConnection, "webConnection cannot be null");
|
||||
this.webClient.setWebConnection(webConnection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Support for MockMvc and HtmlUnitDriver
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
package org.springframework.test.web.servlet.htmlunit.webdriver;
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.HttpWebConnection;
|
||||
import com.gargoylesoftware.htmlunit.Page;
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import com.gargoylesoftware.htmlunit.WebConnection;
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import com.gargoylesoftware.htmlunit.WebResponse;
|
||||
import com.gargoylesoftware.htmlunit.WebResponseData;
|
||||
import com.gargoylesoftware.htmlunit.util.NameValuePair;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.sameInstance;
|
||||
import static org.hamcrest.Matchers.isEmptyString;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import static org.mockito.Mockito.*;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import static org.springframework.test.web.servlet.htmlunit.DelegatingWebConnection.DelegateWebConnection;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DelegatingWebConnectionTests {
|
||||
private DelegatingWebConnection webConnection;
|
||||
|
||||
@Mock
|
||||
private WebRequestMatcher matcher1;
|
||||
@Mock
|
||||
private WebRequestMatcher matcher2;
|
||||
@Mock
|
||||
private WebConnection defaultConnection;
|
||||
@Mock
|
||||
private WebConnection connection1;
|
||||
@Mock
|
||||
private WebConnection connection2;
|
||||
|
||||
private WebRequest request;
|
||||
private WebResponse expectedResponse;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
request = new WebRequest(new URL("http://localhost/"));
|
||||
WebResponseData data = new WebResponseData("".getBytes("UTF-8"),200, "", Collections.<NameValuePair>emptyList());
|
||||
expectedResponse = new WebResponse(data, request, 100L);
|
||||
webConnection = new DelegatingWebConnection(defaultConnection, new DelegateWebConnection(matcher1,connection1), new DelegateWebConnection(matcher2,connection2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResponseDefault() throws Exception {
|
||||
when(defaultConnection.getResponse(request)).thenReturn(expectedResponse);
|
||||
|
||||
WebResponse response = webConnection.getResponse(request);
|
||||
|
||||
assertThat(response, sameInstance(expectedResponse));
|
||||
verify(matcher1).matches(request);
|
||||
verify(matcher2).matches(request);
|
||||
verifyNoMoreInteractions(connection1,connection2);
|
||||
verify(defaultConnection).getResponse(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResponseAllMatches() throws Exception {
|
||||
when(matcher1.matches(request)).thenReturn(true);
|
||||
when(matcher2.matches(request)).thenReturn(true);
|
||||
when(connection1.getResponse(request)).thenReturn(expectedResponse);
|
||||
|
||||
WebResponse response = webConnection.getResponse(request);
|
||||
|
||||
assertThat(response, sameInstance(expectedResponse));
|
||||
verify(matcher1).matches(request);
|
||||
verifyNoMoreInteractions(matcher2,connection2,defaultConnection);
|
||||
verify(connection1).getResponse(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResponseSecondMatches() throws Exception {
|
||||
when(matcher2.matches(request)).thenReturn(true);
|
||||
when(connection2.getResponse(request)).thenReturn(expectedResponse);
|
||||
|
||||
WebResponse response = webConnection.getResponse(request);
|
||||
|
||||
assertThat(response, sameInstance(expectedResponse));
|
||||
verify(matcher1).matches(request);
|
||||
verify(matcher2).matches(request);
|
||||
verifyNoMoreInteractions(connection1,defaultConnection);
|
||||
verify(connection2).getResponse(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classlevelJavadoc() throws Exception {
|
||||
WebClient webClient = new WebClient();
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(TestController.class).build();
|
||||
MockMvcWebConnection mockConnection = new MockMvcWebConnection(mockMvc);
|
||||
mockConnection.setWebClient(webClient);
|
||||
|
||||
WebRequestMatcher cdnMatcher = new UrlRegexRequestMatcher(".*?//code.jquery.com/.*");
|
||||
WebConnection httpConnection = new HttpWebConnection(webClient);
|
||||
WebConnection webConnection = new DelegatingWebConnection(mockConnection, new DelegateWebConnection(cdnMatcher, httpConnection));
|
||||
|
||||
webClient.setWebConnection(webConnection);
|
||||
|
||||
Page page = webClient.getPage("http://code.jquery.com/jquery-1.11.0.min.js");
|
||||
assertThat(page.getWebResponse().getStatusCode(), equalTo(200));
|
||||
assertThat(page.getWebResponse().getContentAsString(), not(isEmptyString()));
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class TestController {}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Controller
|
||||
public class ForwardController {
|
||||
|
||||
@RequestMapping("/forward")
|
||||
public String forward() {
|
||||
return "forward:/";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Controller
|
||||
public class HelloController {
|
||||
@RequestMapping
|
||||
@ResponseBody
|
||||
public String header(HttpServletRequest request) {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.test.web.servlet.htmlunit.HostRequestMatcher;
|
||||
import org.springframework.test.web.servlet.htmlunit.WebRequestMatcher;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class HostRequestMatcherTests {
|
||||
|
||||
@Test
|
||||
public void localhostMatches() throws Exception {
|
||||
WebRequestMatcher matcher = new HostRequestMatcher("localhost");
|
||||
|
||||
boolean matches = matcher.matches(new WebRequest(new URL("http://localhost/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(true));;
|
||||
|
||||
matches = matcher.matches(new WebRequest(new URL("http://example.com/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(false));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleHosts() throws Exception {
|
||||
WebRequestMatcher matcher = new HostRequestMatcher("localhost","example.com");
|
||||
|
||||
boolean matches = matcher.matches(new WebRequest(new URL("http://localhost/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(true));;
|
||||
|
||||
matches = matcher.matches(new WebRequest(new URL("http://example.com/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(true));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specificPort() throws Exception {
|
||||
WebRequestMatcher matcher = new HostRequestMatcher("localhost:8080");
|
||||
|
||||
boolean matches = matcher.matches(new WebRequest(new URL("http://localhost:8080/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(true));;
|
||||
|
||||
matches = matcher.matches(new WebRequest(new URL("http://localhost:9090/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(false));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultPortInMatcher() throws Exception {
|
||||
WebRequestMatcher matcher = new HostRequestMatcher("localhost:80");
|
||||
|
||||
boolean matches = matcher.matches(new WebRequest(new URL("http://localhost:80/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(true));;
|
||||
|
||||
matches = matcher.matches(new WebRequest(new URL("http://localhost/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(true));;
|
||||
|
||||
matches = matcher.matches(new WebRequest(new URL("http://localhost:9090/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(false));;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,782 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.CookieManager;
|
||||
import com.gargoylesoftware.htmlunit.HttpMethod;
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import com.gargoylesoftware.htmlunit.util.NameValuePair;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class HtmlUnitRequestBuilderTests {
|
||||
private WebRequest webRequest;
|
||||
|
||||
private ServletContext servletContext;
|
||||
|
||||
private Map<String, MockHttpSession> sessions;
|
||||
|
||||
private WebClient webClient;
|
||||
|
||||
private HtmlUnitRequestBuilder requestBuilder;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
sessions = new HashMap<>();
|
||||
webRequest = new WebRequest(new URL("http://example.com:80/test/this/here"));
|
||||
webRequest.setHttpMethod(HttpMethod.GET);
|
||||
webRequest.setRequestParameters(new ArrayList<>());
|
||||
webClient = new WebClient();
|
||||
requestBuilder = new HtmlUnitRequestBuilder(sessions, webClient, webRequest);
|
||||
servletContext = new MockServletContext();
|
||||
}
|
||||
|
||||
// --- constructor
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorNullSessions() {
|
||||
new HtmlUnitRequestBuilder(null, webClient, webRequest);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorNullWebClient() {
|
||||
new HtmlUnitRequestBuilder(sessions, null, webRequest);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorNullWebRequest() {
|
||||
new HtmlUnitRequestBuilder(sessions, webClient, null);
|
||||
}
|
||||
|
||||
// --- buildRequest
|
||||
|
||||
@Test
|
||||
public void buildRequestBasicAuth() {
|
||||
String base64Credentials = "dXNlcm5hbWU6cGFzc3dvcmQ=";
|
||||
String authzHeaderValue = "Basic: " + base64Credentials;
|
||||
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(base64Credentials);
|
||||
webRequest.setCredentials(credentials);
|
||||
webRequest.setAdditionalHeader("Authorization", authzHeaderValue);
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getAuthType(), equalTo("Basic"));
|
||||
assertThat(actualRequest.getHeader("Authorization"), equalTo(authzHeaderValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestCharacterEncoding() {
|
||||
String charset = "UTF-8";
|
||||
webRequest.setCharset(charset);
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getCharacterEncoding(), equalTo(charset));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestDefaultCharacterEncoding() {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getCharacterEncoding(), equalTo("ISO-8859-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestContentLength() {
|
||||
String content = "some content that has length";
|
||||
webRequest.setHttpMethod(HttpMethod.POST);
|
||||
webRequest.setRequestBody(content);
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getContentLength(), equalTo(content.length()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestContentType() {
|
||||
String contentType = "text/html;charset=UTF-8";
|
||||
webRequest.setAdditionalHeader("Content-Type", contentType);
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getContentType(), equalTo(contentType));
|
||||
assertThat(actualRequest.getHeader("Content-Type"), equalTo(contentType));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestContextPathUsesFirstSegmentByDefault() {
|
||||
String contextPath = requestBuilder.buildRequest(servletContext).getContextPath();
|
||||
|
||||
assertThat(contextPath, equalTo("/test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestContextPathUsesNoFirstSegmentWithDefault() throws MalformedURLException {
|
||||
webRequest.setUrl(new URL("http://example.com/"));
|
||||
String contextPath = requestBuilder.buildRequest(servletContext).getContextPath();
|
||||
|
||||
assertThat(contextPath, equalTo(""));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void buildRequestContextPathInvalid() {
|
||||
requestBuilder.setContextPath("/invalid");
|
||||
|
||||
requestBuilder.buildRequest(servletContext).getContextPath();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestContextPathEmpty() {
|
||||
String expected = "";
|
||||
requestBuilder.setContextPath(expected);
|
||||
|
||||
String contextPath = requestBuilder.buildRequest(servletContext).getContextPath();
|
||||
|
||||
assertThat(contextPath, equalTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestContextPathExplicit() {
|
||||
String expected = "/test";
|
||||
requestBuilder.setContextPath(expected);
|
||||
|
||||
String contextPath = requestBuilder.buildRequest(servletContext).getContextPath();
|
||||
|
||||
assertThat(contextPath, equalTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestContextPathMulti() {
|
||||
String expected = "/test/this";
|
||||
requestBuilder.setContextPath(expected);
|
||||
|
||||
String contextPath = requestBuilder.buildRequest(servletContext).getContextPath();
|
||||
|
||||
assertThat(contextPath, equalTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestCookiesNull() {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getCookies(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestCookiesSingle() {
|
||||
webRequest.setAdditionalHeader("Cookie", "name=value");
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
Cookie[] cookies = actualRequest.getCookies();
|
||||
assertThat(cookies.length, equalTo(1));
|
||||
assertThat(cookies[0].getName(), equalTo("name"));
|
||||
assertThat(cookies[0].getValue(), equalTo("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestCookiesMulti() {
|
||||
webRequest.setAdditionalHeader("Cookie", "name=value; name2=value2");
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
Cookie[] cookies = actualRequest.getCookies();
|
||||
assertThat(cookies.length, equalTo(2));
|
||||
Cookie cookie = cookies[0];
|
||||
assertThat(cookie.getName(), equalTo("name"));
|
||||
assertThat(cookie.getValue(), equalTo("value"));
|
||||
cookie = cookies[1];
|
||||
assertThat(cookie.getName(), equalTo("name2"));
|
||||
assertThat(cookie.getValue(), equalTo("value2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestInputStream() throws Exception {
|
||||
String content = "some content that has length";
|
||||
webRequest.setHttpMethod(HttpMethod.POST);
|
||||
webRequest.setRequestBody(content);
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(IOUtils.toString(actualRequest.getInputStream()), equalTo(content));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocalAddr() {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocalAddr(), equalTo("127.0.0.1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocaleDefault() {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocale(), equalTo(Locale.getDefault()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocaleDa() {
|
||||
webRequest.setAdditionalHeader("Accept-Language", "da");
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocale(), equalTo(new Locale("da")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocaleEnGbQ08() {
|
||||
webRequest.setAdditionalHeader("Accept-Language", "en-gb;q=0.8");
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocale(), equalTo(new Locale("en", "gb", "0.8")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocaleEnQ07() {
|
||||
webRequest.setAdditionalHeader("Accept-Language", "en;q=0.7");
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocale(), equalTo(new Locale("en", "", "0.7")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocaleEnUs() {
|
||||
webRequest.setAdditionalHeader("Accept-Language", "en-US");
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocale(), equalTo(Locale.US));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocaleFr() {
|
||||
webRequest.setAdditionalHeader("Accept-Language", "fr");
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocale(), equalTo(Locale.FRENCH));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocaleMulti() {
|
||||
webRequest.setAdditionalHeader("Accept-Language", "da, en-gb;q=0.8, en;q=0.7");
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
// FIXME Locale.ENGLISH is due to fact cannot remove it from MockHttpServletRequest
|
||||
List<Locale> expected = Arrays.asList(new Locale("da"), new Locale("en", "gb", "0.8"), new Locale("en", "",
|
||||
"0.7"), Locale.ENGLISH);
|
||||
assertThat(Collections.list(actualRequest.getLocales()), equalTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocaleName() {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocalName(), equalTo("localhost"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocalPort() {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocalPort(), equalTo(80));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestLocalMissing() throws Exception {
|
||||
webRequest.setUrl(new URL("http://localhost/test/this"));
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getLocalPort(), equalTo(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestMethods() {
|
||||
HttpMethod[] methods = HttpMethod.values();
|
||||
|
||||
for (HttpMethod expectedMethod : methods) {
|
||||
webRequest.setHttpMethod(expectedMethod);
|
||||
String actualMethod = requestBuilder.buildRequest(servletContext).getMethod();
|
||||
assertThat(actualMethod, equalTo(actualMethod));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestParameterMap() throws Exception {
|
||||
setParameter("name", "value");
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getParameterMap().size(), equalTo(1));
|
||||
assertThat(actualRequest.getParameter("name"), equalTo("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestParameterMapQuery() throws Exception {
|
||||
webRequest.setUrl(new URL("http://example.com/example/?name=value"));
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getParameterMap().size(), equalTo(1));
|
||||
assertThat(actualRequest.getParameter("name"), equalTo("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestParameterMapQueryMulti() throws Exception {
|
||||
webRequest.setUrl(new URL("http://example.com/example/?name=value¶m2=value+2"));
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getParameterMap().size(), equalTo(2));
|
||||
assertThat(actualRequest.getParameter("name"), equalTo("value"));
|
||||
assertThat(actualRequest.getParameter("param2"), equalTo("value 2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestPathInfo() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getPathInfo(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestPathInfoNull() throws Exception {
|
||||
webRequest.setUrl(new URL("http://example.com/example"));
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getPathInfo(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestAndAntPathRequestMatcher() throws Exception {
|
||||
webRequest.setUrl(new URL("http://example.com/app/login/authenticate"));
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
// verify it is going to work with Spring Security's AntPathRequestMatcher
|
||||
assertThat(actualRequest.getPathInfo(), nullValue());
|
||||
assertThat(actualRequest.getServletPath(), equalTo("/login/authenticate"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestProtocol() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getProtocol(), equalTo("HTTP/1.1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestQuery() throws Exception {
|
||||
String expectedQuery = "aparam=avalue";
|
||||
webRequest.setUrl(new URL("http://example.com/example?" + expectedQuery));
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getQueryString(), equalTo(expectedQuery));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestReader() throws Exception {
|
||||
String expectedBody = "request body";
|
||||
webRequest.setHttpMethod(HttpMethod.POST);
|
||||
webRequest.setRequestBody(expectedBody);
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(IOUtils.toString(actualRequest.getReader()), equalTo(expectedBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestRemoteAddr() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getRemoteAddr(), equalTo("127.0.0.1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestRemoteHost() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getRemoteAddr(), equalTo("127.0.0.1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestRemotePort() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getRemotePort(), equalTo(80));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestRemotePort8080() throws Exception {
|
||||
webRequest.setUrl(new URL("http://example.com:8080/"));
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getRemotePort(), equalTo(8080));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestRemotePort80WithDefault() throws Exception {
|
||||
webRequest.setUrl(new URL("http://example.com/"));
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getRemotePort(), equalTo(80));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestRequestedSessionId() throws Exception {
|
||||
String sessionId = "session-id";
|
||||
webRequest.setAdditionalHeader("Cookie", "JSESSIONID=" + sessionId);
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getRequestedSessionId(), equalTo(sessionId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestRequestedSessionIdNull() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getRequestedSessionId(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestUri() {
|
||||
String uri = requestBuilder.buildRequest(servletContext).getRequestURI();
|
||||
assertThat(uri, equalTo("/test/this/here"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestUrl() {
|
||||
String uri = requestBuilder.buildRequest(servletContext).getRequestURL().toString();
|
||||
assertThat(uri, equalTo("http://example.com/test/this/here"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSchemeHttp() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getScheme(), equalTo("http"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSchemeHttps() throws Exception {
|
||||
webRequest.setUrl(new URL("https://example.com/"));
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getScheme(), equalTo("https"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestServerName() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getServerName(), equalTo("example.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestServerPort() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getServerPort(), equalTo(80));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestServerPortDefault() throws Exception {
|
||||
webRequest.setUrl(new URL("https://example.com/"));
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getServerPort(), equalTo(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestServletContext() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getServletContext(), equalTo(servletContext));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestServletPath() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getServletPath(), equalTo("/this/here"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSession() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
HttpSession newSession = actualRequest.getSession();
|
||||
assertThat(newSession, notNullValue());
|
||||
assertSingleSessionCookie(
|
||||
"JSESSIONID=" + newSession.getId() + "; Path=/test; Domain=example.com");
|
||||
|
||||
webRequest.setAdditionalHeader("Cookie", "JSESSIONID=" + newSession.getId());
|
||||
|
||||
requestBuilder = new HtmlUnitRequestBuilder(sessions, webClient, webRequest);
|
||||
actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getSession(), sameInstance(newSession));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSessionWithExistingSession() throws Exception {
|
||||
String sessionId = "session-id";
|
||||
webRequest.setAdditionalHeader("Cookie", "JSESSIONID=" + sessionId);
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
HttpSession session = actualRequest.getSession();
|
||||
assertThat(session.getId(), equalTo(sessionId));
|
||||
assertSingleSessionCookie("JSESSIONID=" + session.getId() + "; Path=/test; Domain=example.com");
|
||||
|
||||
requestBuilder = new HtmlUnitRequestBuilder(sessions, webClient, webRequest);
|
||||
actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
assertThat(actualRequest.getSession(), equalTo(session));
|
||||
|
||||
webRequest.setAdditionalHeader("Cookie", "JSESSIONID=" + sessionId + "NEW");
|
||||
actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
assertThat(actualRequest.getSession(), not(equalTo(session)));
|
||||
assertSingleSessionCookie("JSESSIONID=" + actualRequest.getSession().getId()
|
||||
+ "; Path=/test; Domain=example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSessionTrue() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
HttpSession session = actualRequest.getSession(true);
|
||||
assertThat(session, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSessionFalseIsNull() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
HttpSession session = actualRequest.getSession(false);
|
||||
assertThat(session, nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSessionFalseWithExistingSession() throws Exception {
|
||||
String sessionId = "session-id";
|
||||
webRequest.setAdditionalHeader("Cookie", "JSESSIONID=" + sessionId);
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
HttpSession session = actualRequest.getSession(false);
|
||||
assertThat(session, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSessionIsNew() throws Exception {
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getSession().isNew(), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSessionIsNewFalse() throws Exception {
|
||||
String sessionId = "session-id";
|
||||
webRequest.setAdditionalHeader("Cookie", "JSESSIONID=" + sessionId);
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getSession().isNew(), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildRequestSessionInvalidate() throws Exception {
|
||||
String sessionId = "session-id";
|
||||
webRequest.setAdditionalHeader("Cookie", "JSESSIONID=" + sessionId);
|
||||
|
||||
MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
HttpSession sessionToRemove = actualRequest.getSession();
|
||||
sessionToRemove.invalidate();
|
||||
|
||||
assertThat(sessions.containsKey(sessionToRemove.getId()), equalTo(false));
|
||||
assertSingleSessionCookie("JSESSIONID=" + sessionToRemove.getId()
|
||||
+ "; Expires=Thu, 01-Jan-1970 00:00:01 GMT; Path=/test; Domain=example.com");
|
||||
|
||||
webRequest.removeAdditionalHeader("Cookie");
|
||||
requestBuilder = new HtmlUnitRequestBuilder(sessions, webClient, webRequest);
|
||||
|
||||
actualRequest = requestBuilder.buildRequest(servletContext);
|
||||
|
||||
assertThat(actualRequest.getSession().isNew(), equalTo(true));
|
||||
assertThat(sessions.containsKey(sessionToRemove.getId()), equalTo(false));
|
||||
}
|
||||
|
||||
// --- setContextPath
|
||||
|
||||
@Test
|
||||
public void setContextPathNull() {
|
||||
requestBuilder.setContextPath(null);
|
||||
|
||||
assertThat(getContextPath(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setContextPathEmptyString() {
|
||||
requestBuilder.setContextPath("");
|
||||
|
||||
assertThat(getContextPath(), isEmptyString());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setContextPathDoesNotStartWithSlash() {
|
||||
requestBuilder.setContextPath("abc/def");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setContextPathEndsWithSlash() {
|
||||
requestBuilder.setContextPath("/abc/def/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setContextPath() {
|
||||
String expectedContextPath = "/abc/def";
|
||||
requestBuilder.setContextPath(expectedContextPath);
|
||||
|
||||
assertThat(getContextPath(), equalTo(expectedContextPath));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeHeader() throws Exception {
|
||||
String headerName = "PARENT";
|
||||
String headerValue = "VALUE";
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new HelloController())
|
||||
.defaultRequest(get("/").header(headerName, headerValue))
|
||||
.build();
|
||||
|
||||
assertThat(mockMvc.perform(requestBuilder).andReturn().getRequest().getHeader(headerName), equalTo(headerValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeSession() throws Exception {
|
||||
String attrName = "PARENT";
|
||||
String attrValue = "VALUE";
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new HelloController())
|
||||
.defaultRequest(get("/").sessionAttr(attrName, attrValue))
|
||||
.build();
|
||||
|
||||
assertThat(mockMvc.perform(requestBuilder).andReturn().getRequest().getSession().getAttribute(attrName), equalTo(attrValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeSessionNotInitialized() throws Exception {
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new HelloController())
|
||||
.defaultRequest(get("/"))
|
||||
.build();
|
||||
|
||||
assertThat(mockMvc.perform(requestBuilder).andReturn().getRequest().getSession(false), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeParameter() throws Exception {
|
||||
String paramName = "PARENT";
|
||||
String paramValue = "VALUE";
|
||||
String paramValue2 = "VALUE2";
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new HelloController())
|
||||
.defaultRequest(get("/").param(paramName, paramValue, paramValue2))
|
||||
.build();
|
||||
|
||||
assertThat(Arrays.asList(mockMvc.perform(requestBuilder).andReturn().getRequest().getParameterValues(paramName)), contains(paramValue, paramValue2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeCookie() throws Exception {
|
||||
String cookieName = "PARENT";
|
||||
String cookieValue = "VALUE";
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new HelloController())
|
||||
.defaultRequest(get("/").cookie(new Cookie(cookieName,cookieValue)))
|
||||
.build();
|
||||
|
||||
Cookie[] cookies = mockMvc.perform(requestBuilder).andReturn().getRequest().getCookies();
|
||||
assertThat(cookies, notNullValue());
|
||||
assertThat(cookies.length, equalTo(1));
|
||||
Cookie cookie = cookies[0];
|
||||
assertThat(cookie.getName(), equalTo(cookieName));
|
||||
assertThat(cookie.getValue(), equalTo(cookieValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeRequestAttribute() throws Exception {
|
||||
String attrName = "PARENT";
|
||||
String attrValue = "VALUE";
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new HelloController())
|
||||
.defaultRequest(get("/").requestAttr(attrName,attrValue))
|
||||
.build();
|
||||
|
||||
assertThat(mockMvc.perform(requestBuilder).andReturn().getRequest().getAttribute(attrName), equalTo(attrValue));
|
||||
}
|
||||
|
||||
|
||||
private void assertSingleSessionCookie(String expected) {
|
||||
com.gargoylesoftware.htmlunit.util.Cookie jsessionidCookie = webClient.getCookieManager().getCookie("JSESSIONID");
|
||||
if (expected == null || expected.contains("Expires=Thu, 01-Jan-1970 00:00:01 GMT")) {
|
||||
assertThat(jsessionidCookie, nullValue());
|
||||
return;
|
||||
}
|
||||
String actual = jsessionidCookie.getValue();
|
||||
assertThat("JSESSIONID=" + actual +
|
||||
"; Path=/test; Domain=example.com", equalTo(expected));
|
||||
}
|
||||
|
||||
private void setParameter(String name, String value) {
|
||||
webRequest.getRequestParameters().add(new NameValuePair(name, value));
|
||||
}
|
||||
|
||||
private String getContextPath() {
|
||||
return (String) ReflectionTestUtils.getField(requestBuilder, "contextPath");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebConnection;
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import com.gargoylesoftware.htmlunit.WebResponse;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class MockMvcConnectionBuilderSupportTests {
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
MockMvc mockMvc;
|
||||
|
||||
WebConnection delegateConnection;
|
||||
|
||||
WebConnection connection;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
delegateConnection = mock(WebConnection.class);
|
||||
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
|
||||
|
||||
connection = new MockMvcWebConnectionBuilderSupport(mockMvc){}
|
||||
.createConnection(delegateConnection);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorMockMvcNull() {
|
||||
new MockMvcWebConnectionBuilderSupport((MockMvc)null){};
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorContextNull() {
|
||||
new MockMvcWebConnectionBuilderSupport((WebApplicationContext)null){};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void context() throws Exception {
|
||||
connection = new MockMvcWebConnectionBuilderSupport(context){}
|
||||
.createConnection(delegateConnection);
|
||||
|
||||
assertMvcProcessed("http://localhost/");
|
||||
assertDelegateProcessed("http://example.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mockMvc() throws Exception {
|
||||
assertMvcProcessed("http://localhost/");
|
||||
assertDelegateProcessed("http://example.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mockMvcExampleDotCom() throws Exception {
|
||||
connection = new MockMvcWebConnectionBuilderSupport(context){}
|
||||
.useMockMvcForHosts("example.com")
|
||||
.createConnection(delegateConnection);
|
||||
|
||||
assertMvcProcessed("http://localhost/");
|
||||
assertMvcProcessed("http://example.com/");
|
||||
assertDelegateProcessed("http://other.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mockMvcAlwaysUseMockMvc() throws Exception {
|
||||
connection = new MockMvcWebConnectionBuilderSupport(context){}
|
||||
.alwaysUseMockMvc()
|
||||
.createConnection(delegateConnection);
|
||||
|
||||
assertMvcProcessed("http://other.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultContextPathEmpty() throws Exception {
|
||||
connection = new MockMvcWebConnectionBuilderSupport(context){}
|
||||
.createConnection(delegateConnection);
|
||||
|
||||
assertThat(getWebResponse("http://localhost/abc").getContentAsString(), equalTo(""));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultContextPathCustom() throws Exception {
|
||||
connection = new MockMvcWebConnectionBuilderSupport(context) {
|
||||
}.contextPath("/abc").createConnection(delegateConnection);
|
||||
|
||||
assertThat(getWebResponse("http://localhost/abc/def").getContentAsString(), equalTo("/abc"));;
|
||||
}
|
||||
|
||||
private void assertMvcProcessed(String url) throws Exception {
|
||||
assertThat(getWebResponse(url), notNullValue());
|
||||
}
|
||||
|
||||
private void assertDelegateProcessed(String url) throws Exception {
|
||||
assertThat(getWebResponse(url), nullValue());
|
||||
}
|
||||
|
||||
private WebResponse getWebResponse(String url) throws IOException {
|
||||
return connection.getResponse(new WebRequest(new URL(url)));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
static class Config {
|
||||
@RestController
|
||||
static class ContextPathController {
|
||||
@RequestMapping
|
||||
public String contextPath(HttpServletRequest request) {
|
||||
return request.getContextPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import com.gargoylesoftware.htmlunit.WebResponse;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class MockMvcWebClientBuilderTests {
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
MockMvc mockMvc;
|
||||
|
||||
WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
|
||||
webClient = new WebClient();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void mockMvcSetupNull() {
|
||||
MockMvcWebClientBuilder.mockMvcSetup(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void webAppContextSetupNull() {
|
||||
MockMvcWebClientBuilder.webAppContextSetup(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mockMvcSetupconfigureWebClient() throws Exception {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
.configureWebClient(webClient);
|
||||
|
||||
assertMvcProcessed("http://localhost/test");
|
||||
assertDelegateProcessed("http://example.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mockMvcSetupCreateWebClient() throws Exception {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
.createWebClient();
|
||||
|
||||
assertMvcProcessed("http://localhost/test");
|
||||
assertDelegateProcessed("http://example.com/");
|
||||
}
|
||||
|
||||
private void assertMvcProcessed(String url) throws Exception {
|
||||
assertThat(getWebResponse(url).getContentAsString(), equalTo("mvc"));;
|
||||
}
|
||||
|
||||
private void assertDelegateProcessed(String url) throws Exception {
|
||||
assertThat(getWebResponse(url).getContentAsString(), not(equalTo("mvc")));
|
||||
}
|
||||
|
||||
private WebResponse getWebResponse(String url) throws IOException {
|
||||
return webClient.getWebConnection().getResponse(new WebRequest(new URL(url)));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
static class Config {
|
||||
@RestController
|
||||
static class ContextPathController {
|
||||
@RequestMapping
|
||||
public String contextPath(HttpServletRequest request) {
|
||||
return "mvc";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.Page;
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class MockMvcWebConnectionTests {
|
||||
MockMvc mockMvc;
|
||||
|
||||
WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mockMvc = MockMvcBuilders
|
||||
.standaloneSetup(new HelloController(), new ForwardController())
|
||||
.build();
|
||||
|
||||
webClient = new WebClient();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextPathNull() throws IOException {
|
||||
webClient.setWebConnection(new MockMvcWebConnection(mockMvc, null));
|
||||
|
||||
Page page = webClient.getPage("http://localhost/context/a");
|
||||
|
||||
assertThat(page.getWebResponse().getStatusCode(), equalTo(200));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextPathExplicit() throws IOException {
|
||||
webClient.setWebConnection(new MockMvcWebConnection(mockMvc, "/context"));
|
||||
|
||||
Page page = webClient.getPage("http://localhost/context/a");
|
||||
|
||||
assertThat(page.getWebResponse().getStatusCode(), equalTo(200));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextPathEmpty() throws IOException {
|
||||
webClient.setWebConnection(new MockMvcWebConnection(mockMvc, ""));
|
||||
|
||||
Page page = webClient.getPage("http://localhost/context/a");
|
||||
|
||||
assertThat(page.getWebResponse().getStatusCode(), equalTo(200));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forward() throws IOException {
|
||||
webClient.setWebConnection(new MockMvcWebConnection(mockMvc, ""));
|
||||
|
||||
Page page = webClient.getPage("http://localhost/forward");
|
||||
|
||||
assertThat(page.getWebResponse().getContentAsString(), equalTo("hello"));;
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void contextPathDoesNotStartWithSlash() throws IOException {
|
||||
new MockMvcWebConnection(mockMvc, "context");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void contextPathEndsWithSlash() throws IOException {
|
||||
new MockMvcWebConnection(mockMvc, "/context/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import com.gargoylesoftware.htmlunit.WebResponse;
|
||||
import com.gargoylesoftware.htmlunit.util.NameValuePair;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class MockWebResponseBuilderTests {
|
||||
|
||||
private WebRequest webRequest;
|
||||
|
||||
private MockHttpServletResponse httpServletResponse;
|
||||
|
||||
private MockWebResponseBuilder responseBuilder;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
webRequest = new WebRequest(new URL("http://example.com:80/test/this/here"));
|
||||
httpServletResponse = new MockHttpServletResponse();
|
||||
|
||||
responseBuilder = new MockWebResponseBuilder(System.currentTimeMillis(), webRequest, httpServletResponse);
|
||||
}
|
||||
|
||||
// --- constructor
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorNullWebRequest() {
|
||||
new MockWebResponseBuilder(0L, null, httpServletResponse);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorNullResponse() throws Exception {
|
||||
new MockWebResponseBuilder(0L, new WebRequest(new URL("http://example.com:80/test/this/here")), null);
|
||||
}
|
||||
|
||||
// --- build
|
||||
|
||||
@Test
|
||||
public void buildContent() throws Exception {
|
||||
httpServletResponse.getWriter().write("expected content");
|
||||
|
||||
WebResponse webResponse = responseBuilder.build();
|
||||
|
||||
assertThat(webResponse.getContentAsString(), equalTo("expected content"));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildContentCharset() throws Exception {
|
||||
httpServletResponse.addHeader("Content-Type", "text/html; charset=UTF-8");
|
||||
WebResponse webResponse = responseBuilder.build();
|
||||
|
||||
assertThat(webResponse.getContentCharset(), equalTo("UTF-8"));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildContentType() throws Exception {
|
||||
httpServletResponse.addHeader("Content-Type", "text/html; charset-UTF-8");
|
||||
WebResponse webResponse = responseBuilder.build();
|
||||
|
||||
assertThat(webResponse.getContentType(), equalTo("text/html"));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildResponseHeaders() throws Exception {
|
||||
httpServletResponse.addHeader("Content-Type", "text/html");
|
||||
httpServletResponse.addHeader("X-Test", "value");
|
||||
WebResponse webResponse = responseBuilder.build();
|
||||
|
||||
List<NameValuePair> responseHeaders = webResponse.getResponseHeaders();
|
||||
assertThat(responseHeaders.size(), equalTo(2));;
|
||||
NameValuePair header = responseHeaders.get(0);
|
||||
assertThat(header.getName(), equalTo("Content-Type"));;
|
||||
assertThat(header.getValue(), equalTo("text/html"));;
|
||||
header = responseHeaders.get(1);
|
||||
assertThat(header.getName(), equalTo("X-Test"));;
|
||||
assertThat(header.getValue(), equalTo("value"));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildStatus() throws Exception {
|
||||
WebResponse webResponse = responseBuilder.build();
|
||||
|
||||
assertThat(webResponse.getStatusCode(), equalTo(200));;
|
||||
assertThat(webResponse.getStatusMessage(), equalTo("OK"));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildStatusNotOk() throws Exception {
|
||||
httpServletResponse.setStatus(401);
|
||||
WebResponse webResponse = responseBuilder.build();
|
||||
|
||||
assertThat(webResponse.getStatusCode(), equalTo(401));;
|
||||
assertThat(webResponse.getStatusMessage(), equalTo("Unauthorized"));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildStatusCustomMessage() throws Exception {
|
||||
httpServletResponse.sendError(401, "Custom");
|
||||
WebResponse webResponse = responseBuilder.build();
|
||||
|
||||
assertThat(webResponse.getStatusCode(), equalTo(401));;
|
||||
assertThat(webResponse.getStatusMessage(), equalTo("Custom"));;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildWebRequest() throws Exception {
|
||||
WebResponse webResponse = responseBuilder.build();
|
||||
|
||||
assertThat(webResponse.getWebRequest(), equalTo(webRequest));;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.test.web.servlet.htmlunit.UrlRegexRequestMatcher;
|
||||
import org.springframework.test.web.servlet.htmlunit.WebRequestMatcher;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class UrlRegexRequestMatcherTests {
|
||||
|
||||
@Test
|
||||
public void classlevelJavadoc() throws Exception {
|
||||
WebRequestMatcher cdnMatcher = new UrlRegexRequestMatcher(".*?//code.jquery.com/.*");
|
||||
|
||||
boolean matches = cdnMatcher.matches(new WebRequest(new URL("http://code.jquery.com/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(true));
|
||||
|
||||
matches = cdnMatcher.matches(new WebRequest(new URL("http://localhost/jquery-1.11.0.min.js")));
|
||||
assertThat(matches, equalTo(false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.test.web.servlet.htmlunit.webdriver;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class MockMvcHtmlUnitDriverBuilderTests {
|
||||
|
||||
public static final String EXPECTED_BODY = "MockMvcHtmlUnitDriverBuilderTests mvc";
|
||||
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
MockMvc mockMvc;
|
||||
|
||||
HtmlUnitDriver driver;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void mockMvcSetupNull() {
|
||||
MockMvcHtmlUnitDriverBuilder.mockMvcSetup(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void webAppContextSetupNull() {
|
||||
MockMvcHtmlUnitDriverBuilder.webAppContextSetup(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mockMvcSetupConfigureDriver() throws Exception {
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
.configureDriver(new WebConnectionHtmlUnitDriver());
|
||||
|
||||
assertMvcProcessed("http://localhost/test");
|
||||
assertDelegateProcessed("http://example.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mockMvcSetupCreateDriver() throws Exception {
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
.createDriver();
|
||||
|
||||
assertMvcProcessed("http://localhost/test");
|
||||
assertDelegateProcessed("http://example.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javascriptEnabledDefaultEnabled() {
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
.createDriver();
|
||||
|
||||
assertThat(driver.isJavascriptEnabled(), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javascriptEnabledDisabled() {
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
.javascriptEnabled(false)
|
||||
.createDriver();
|
||||
|
||||
assertThat(driver.isJavascriptEnabled(), equalTo(false));
|
||||
}
|
||||
|
||||
private void assertMvcProcessed(String url) throws Exception {
|
||||
assertThat(get(url), containsString(EXPECTED_BODY));
|
||||
}
|
||||
|
||||
private void assertDelegateProcessed(String url) throws Exception {
|
||||
assertThat(get(url), not(containsString(EXPECTED_BODY)));
|
||||
}
|
||||
|
||||
private String get(String url) throws IOException {
|
||||
driver.get(url);
|
||||
return driver.getPageSource();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
static class Config {
|
||||
@RestController
|
||||
static class ContextPathController {
|
||||
@RequestMapping
|
||||
public String contextPath(HttpServletRequest request) {
|
||||
return EXPECTED_BODY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.springframework.test.web.servlet.htmlunit.webdriver;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebConnection;
|
||||
import com.gargoylesoftware.htmlunit.WebRequest;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.fail;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import static org.mockito.Matchers.any;
|
||||
import org.mockito.Mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
// tag::junit-spring-setup[]
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class WebConnectionHtmlUnitDriverTests {
|
||||
@Mock
|
||||
WebConnection connection;
|
||||
|
||||
WebConnectionHtmlUnitDriver driver;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
driver = new WebConnectionHtmlUnitDriver();
|
||||
|
||||
when(connection.getResponse(any(WebRequest.class))).thenThrow(new InternalError(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWebConnectionDefaultNotNull() {
|
||||
assertThat(driver.getWebConnection(), notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setWebConnection() {
|
||||
driver.setWebConnection(connection);
|
||||
|
||||
assertThat(driver.getWebConnection(), equalTo(connection));
|
||||
try {
|
||||
driver.get("https://example.com");
|
||||
fail("Expected Exception");
|
||||
} catch (InternalError success) {}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setWebConnectionNull() {
|
||||
driver.setWebConnection(null);
|
||||
}
|
||||
}
|
||||
@@ -4154,6 +4154,778 @@ https://github.com/spring-projects/spring-mvc-showcase[spring-mvc-showcase] has
|
||||
coverage based on Spring MVC Test.
|
||||
|
||||
|
||||
[[spring-mvc-test-server-htmlunit]]
|
||||
==== HtmlUnit Integration
|
||||
|
||||
Spring provides integration between <<spring-mvc-test-server,MockMvc>> and
|
||||
http://htmlunit.sourceforge.net/[HtmlUnit]. This simplifies performing end to end testing
|
||||
when using HTML based views. This integration enables developers to:
|
||||
|
||||
* Easily test pages using tools (i.e. http://htmlunit.sourceforge.net/[HtmlUnit],
|
||||
http://seleniumhq.org/projects/webdriver/[WebDriver], &
|
||||
http://www.gebish.org/manual/current/testing.html#spock_junit__testng[Geb]) that we
|
||||
already use for integration testing without starting an application server
|
||||
* Support testing of JavaScript
|
||||
* Optionally test using mock services to speed up testing.
|
||||
* Share logic between end-to-end tests and integration tests
|
||||
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
MockMvc will work with templating technologies that do not rely on a Servlet Container
|
||||
(i.e. Thymeleaf, Freemarker, Velocity, etc). It does not work with JSPs since they rely on
|
||||
the Servlet Container.
|
||||
====
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-why]]
|
||||
===== Why HtmlUnit Integration?
|
||||
|
||||
The most obvious question that comes to mind is "Why do I need this?" The answer is best
|
||||
found by exploring a very basic sample application. Assume you have a Spring MVC web
|
||||
application that allows CRUD operations on a `Message` object. The application also allows
|
||||
paging through all messages. How would you go about testing it?
|
||||
|
||||
With Spring MVC Test, we can easily test if we are able to create a `Message`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
MockHttpServletRequestBuilder createMessage = post("/messages/")
|
||||
.param("summary", "Spring Rocks")
|
||||
.param("text", "In case you didn't know, Spring Rocks!");
|
||||
|
||||
mockMvc.perform(createMessage)
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("/messages/123"));
|
||||
----
|
||||
|
||||
What if we want to test our form view that allows us to create the message? For example,
|
||||
assume our form looks like the following snippet:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<form id="messageForm" action="/messages/" method="post">
|
||||
<div class="pull-right"><a href="/messages/">Messages</a></div>
|
||||
|
||||
<label for="summary">Summary</label>
|
||||
<input type="text" class="required" id="summary" name="summary" value="" />
|
||||
|
||||
<label for="text">Message</label>
|
||||
<textarea id="text" name="text"></textarea>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="Create" />
|
||||
</div>
|
||||
</form>
|
||||
----
|
||||
|
||||
How do we ensure that our form will produce the correct request to create a new message? A
|
||||
naive attempt would look like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
mockMvc.perform(get("/messages/form"))
|
||||
.andExpect(xpath("//input[@name='summary']").exists())
|
||||
.andExpect(xpath("//textarea[@name='text']").exists());
|
||||
----
|
||||
|
||||
This test has some obvious problems. If we updated our controller to use the parameter
|
||||
"message" instead of "text", our test would would incorrectly pass. To resolve this we
|
||||
could combine our two tests:
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-mock-mvc-test]]
|
||||
[source,java]
|
||||
----
|
||||
String summaryParamName = "summary";
|
||||
String textParamName = "text";
|
||||
mockMvc.perform(get("/messages/form"))
|
||||
.andExpect(xpath("//input[@name='" + summaryParamName + "']").exists())
|
||||
.andExpect(xpath("//textarea[@name='" + textParamName + "']").exists());
|
||||
|
||||
MockHttpServletRequestBuilder createMessage = post("/messages/")
|
||||
.param(summaryParamName, "Spring Rocks")
|
||||
.param(textParamName, "In case you didn't know, Spring Rocks!");
|
||||
|
||||
mockMvc.perform(createMessage)
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("/messages/123"));
|
||||
----
|
||||
|
||||
This would reduce the risk of our test incorrectly passing, but there are still some
|
||||
problems:
|
||||
|
||||
* What if we had multiple forms on our page? Admittedly we could update our xpath
|
||||
expressions, but they get more complicated the more factors we take into account (are the
|
||||
fields the correct type, are the fields enabled, etc).
|
||||
* Another issue is that we are doing double the work we would expect.
|
||||
We must first verify the view and then we submit the view with the same parameters we just
|
||||
verified.
|
||||
Ideally this could be done all at once.
|
||||
* Last, there are some things that we still cannot account for. For example, what if the
|
||||
form has JavaScript validation that we wish to validate too?
|
||||
|
||||
The overall problem is that testing a web page is not a single interaction. Instead, it is
|
||||
a combination of how the user interacts with a web page and how that web page interacts
|
||||
with other resources. For example, the result of form view is used as an input to a user
|
||||
for creating a message. Another example is that our form view utilizes additional
|
||||
resources, like JavaScript validation, that impact the behavior of the page.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-why-integration]]
|
||||
====== Integration testing to the rescue?
|
||||
|
||||
To resolve the issues above we could perform integration testing, but this has some
|
||||
obvious drawbacks. Consider testing the view that allows us to page through the messages.
|
||||
We might need the following tests:
|
||||
|
||||
* Does our page display a message to the user indicating that no results are available
|
||||
when the messages are empty?
|
||||
* Does our page properly display a single message?
|
||||
* Does our page properly support paging?
|
||||
|
||||
To set these tests up we would need to ensure our database contained the proper messages
|
||||
in it. This leads to a number of problems:
|
||||
|
||||
* Ensuring the proper messages are in the database can be tedious (think possible foreign
|
||||
keys).
|
||||
* Testing would be slow since each test would require ensuring the database was in the
|
||||
correct state.
|
||||
* Since our database needs to be in a specific state, we cannot run the test in parallel.
|
||||
* Assertions on things like auto generated ids, timestamps, etc can be challenging.
|
||||
|
||||
These problems do not mean that we should abandon integration testing all together.
|
||||
Instead, we can reduce the number of integration tests by moving our detailed tests to use
|
||||
mock services which will perform much faster. We can then use fewer integration tests that
|
||||
validate simple workflows to ensure that everything works together properly.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-why-mockmvc]]
|
||||
====== Enter HtmlUnit Integration
|
||||
|
||||
So how can we provide a balance between testing the interactions of our pages and still
|
||||
get performance? I'm sure you already guessed it...integrating with HtmlUnit
|
||||
will allow us to:
|
||||
|
||||
* Easily test our pages using tools (i.e. HtmlUnit, WebDriver, & Geb) that we already use
|
||||
for integration testing without starting an application server
|
||||
* Support testing of JavaScript
|
||||
* Optionally test using mock services to speed up testing.
|
||||
* Share logic between end-to-end tests and integration tests
|
||||
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-options]]
|
||||
====== HtmlUnit Integration Options
|
||||
|
||||
There are a number of ways to integrate with HtmlUnit. You can find a summary below:
|
||||
|
||||
* <<spring-mvc-test-server-htmlunit-mah,MockMvc and HtmlUnit>> - Use this option if you want the raw libraries
|
||||
* <<spring-mvc-test-server-htmlunit-webdriver,MockMvc and WebDriver>> - Use this option to ease development and be able to reuse code
|
||||
between integration and end-to-end testing.
|
||||
* <<spring-mvc-test-server-htmlunit-geb,MockMvc and Geb>> - Use this option if you like using Groovy for testing, would like to
|
||||
ease development, and be able to reuse code between integration and end-to-end testing.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-mah]]
|
||||
===== MockMvc and HtmlUnit
|
||||
|
||||
This section describes how to integrate `MockMvc` and HtmlUnit. Use this option if you
|
||||
want to use the raw HtmlUnit libraries.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-mah-setup]]
|
||||
====== MockMvc and HtmlUnit Setup
|
||||
|
||||
We can easily create an HtmlUnit `WebClient` that integrates with `MockMvc` using the
|
||||
following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.webAppContextSetup(context)
|
||||
.createWebClient();
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
This is a simple example of using `MockMvcWebClientBuilder`. For advanced usage see
|
||||
<<Advanced MockMvcWebClientBuilder>>
|
||||
====
|
||||
|
||||
This will ensure any URL that has a host of "localhost" will be directed at our MockMvc
|
||||
instance without the need for HTTP. Any other URL will be requested as normal. This allows
|
||||
for easily testing with the use of CDNs.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-mah-usage]]
|
||||
====== MockMvc and HtmlUnit Usage
|
||||
|
||||
Now we can use HtmlUnit as we normally would, but without the need to deploy our
|
||||
application. For example, we can request the view to create a message with the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form");
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The the context path is "". Alternatively, we could have specified the context path as
|
||||
illustrated in <<Advanced MockMvcWebClientBuilder>>.
|
||||
====
|
||||
|
||||
We can then fill out the form and submit it to create a message.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");
|
||||
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
|
||||
summaryInput.setValueAttribute("Spring Rocks");
|
||||
HtmlTextArea textInput = createMsgFormPage.getHtmlElementById("text");
|
||||
textInput.setText("In case you didn't know, Spring Rocks!");
|
||||
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");
|
||||
HtmlPage newMessagePage = submit.click();
|
||||
----
|
||||
|
||||
Finally, we can verify that a new message was created successfully
|
||||
|
||||
[source,java]
|
||||
----
|
||||
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");
|
||||
String id = newMessagePage.getHtmlElementById("id").getTextContent();
|
||||
assertThat(id).isEqualTo("123");
|
||||
String summary = newMessagePage.getHtmlElementById("summary").getTextContent();
|
||||
assertThat(summary).isEqualTo("Spring Rocks");
|
||||
String text = newMessagePage.getHtmlElementById("text").getTextContent();
|
||||
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");
|
||||
----
|
||||
|
||||
This improves on our <<spring-mvc-test-server-htmlunit-mock-mvc-test,MockMvc test>> in a
|
||||
number of ways. First we no longer have to explicitly verify our form and then create a
|
||||
request that looks like the form. Instead, we request the form, fill it out, and submit
|
||||
it. This reduces the overhead significantly.
|
||||
|
||||
Another important factor is that
|
||||
http://htmlunit.sourceforge.net/javascript.html[HtmlUnit uses Mozilla Rhino engine] to
|
||||
evaluate JavaScript on your pages. This means, that we can verify our JavaScript methods
|
||||
as well!
|
||||
|
||||
Refer to the http://htmlunit.sourceforge.net/gettingStarted.html[HtmlUnit documentation]
|
||||
for additional information about using HtmlUnit.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-mah-advanced-builder]]
|
||||
====== Advanced MockMvcWebClientBuilder
|
||||
|
||||
In our example above we used `MockMvcWebClientBuilder` in the simplest way possible.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.webAppContextSetup(context)
|
||||
.createWebClient();
|
||||
}
|
||||
----
|
||||
|
||||
We could also specify some optional arguments:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Before
|
||||
public void setup() {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
// demonstrates applying a MockMvcConfigurer (Spring Security)
|
||||
.webAppContextSetup(context, springSecurity())
|
||||
// for illustration only - defaults to ""
|
||||
.contextPath("")
|
||||
// By default MockMvc is used for localhost only
|
||||
// the following will use MockMvc for example.com and example.org too
|
||||
.useMockMvcForHosts("example.com","example.org")
|
||||
.createWebClient();
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
We could also perform the exact same setup using the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
MockMvc mockMvc = MockMvcBuilders
|
||||
.webAppContextSetup(context)
|
||||
.build();
|
||||
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
// for illustration only - defaults to ""
|
||||
.contextPath("")
|
||||
// By default MockMvc is used for localhost only
|
||||
// the following will use MockMvc for example.com and example.org too
|
||||
.useMockMvcForHosts("example.com","example.org")
|
||||
.createWebClient();
|
||||
----
|
||||
|
||||
This is more verbose, but by building the `WebClient` with a `MockMvc` instance we have
|
||||
the full power of `MockMvc` at our finger tips. Ultimately, this is simply performing the
|
||||
following:
|
||||
|
||||
[TIP]
|
||||
====
|
||||
For additional information on creating a `MockMvc` instance refer to
|
||||
<<spring-mvc-test-server-setup-options>>.
|
||||
====
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-webdriver]]
|
||||
===== MockMvc and WebDriver
|
||||
|
||||
In the previous section, we have already seen how to use MockMvc with HtmlUnit.
|
||||
In this section, we will leverage additional abstractions within
|
||||
http://docs.seleniumhq.org/projects/webdriver/[WebDriver] to make things even easier.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-webdriver-why]]
|
||||
====== Why WebDriver and MockMvc?
|
||||
|
||||
We can already use HtmlUnit and MockMvc, so why would we want to use WebDriver? WebDriver
|
||||
provides a very elegant API and allows us to easily organize our code. To better
|
||||
understand, let's explore an example.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Despite being a part of http://docs.seleniumhq.org/[Selenium], WebDriver does not require
|
||||
a Selenium Server to run your tests.
|
||||
====
|
||||
|
||||
Suppose we need to ensure that a message is created properly. The tests involve finding
|
||||
the html inputs, filling them out, and making various assertions.
|
||||
|
||||
There are many tests because we want to test error conditions as well. For example, we
|
||||
want to ensure that if we fill out only part of the form we get an error. If we fill out
|
||||
the entire form, the newly created message is displayed afterwards.
|
||||
|
||||
If one of the fields was named "summary", then we might have something like the following
|
||||
repeated everywhere within our tests:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
|
||||
summaryInput.setValueAttribute(summary);
|
||||
----
|
||||
|
||||
So what happens if we change the id to be "smmry".
|
||||
This means we would have to update all of our tests! Instead we would hope that we wrote a
|
||||
bit more elegant code where filling out the form was in its own method:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
|
||||
setSummary(currentPage, summary);
|
||||
...
|
||||
}
|
||||
|
||||
public void setSummary(HtmlPage currentPage, String summary) {
|
||||
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
|
||||
summaryInput.setValueAttribute(summary);
|
||||
}
|
||||
----
|
||||
|
||||
This ensures that if we change the UI we do not have to update all of our tests.
|
||||
|
||||
We might take it a step further and place this logic within an Object that represents the
|
||||
`HtmlPage` we are currently on.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public class CreateMessagePage {
|
||||
HtmlPage currentPage;
|
||||
|
||||
HtmlTextInput summaryInput;
|
||||
|
||||
HtmlSubmitInput submit;
|
||||
|
||||
public CreateMessagePage(HtmlPage currentPage) {
|
||||
this.currentPage = currentPage;
|
||||
this.summaryInput = currentPage.getHtmlElementById("summary");
|
||||
this.submit = currentPage.getHtmlElementById("submit");
|
||||
}
|
||||
|
||||
public <T> T createMessage(String summary, String text) throws Exception {
|
||||
setSummary(summary);
|
||||
|
||||
HtmlPage result = submit.click();
|
||||
boolean error = CreateMessagePage.at(result);
|
||||
|
||||
return (T) (error ? new CreateMessagePage(result) : new ViewMessagePage(result));
|
||||
}
|
||||
|
||||
public void setSummary(String summary) throws Exception {
|
||||
summaryInput.setValueAttribute(summary);
|
||||
}
|
||||
|
||||
public static boolean at(HtmlPage page) {
|
||||
return "Create Message".equals(page.getTitleText());
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Formerly, this pattern is known as the
|
||||
https://code.google.com/p/selenium/wiki/PageObjects[Page Object Pattern]. While we can
|
||||
certainly do this with HtmlUnit, WebDriver provides some tools that we will explore in the
|
||||
following sections make this pattern much easier.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-webdriver-setup]]
|
||||
====== MockMvc and WebDriver Setup
|
||||
|
||||
We can easily create a WebDriver implementation that integrates with MockMvc using the
|
||||
following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
WebDriver driver;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
.webAppContextSetup(context)
|
||||
.createDriver();
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
This is a simple example of using `MockMvcHtmlUnitDriverBuilder`.
|
||||
For more advanced usage, refer to <<Advanced MockMvcHtmlUnitDriverBuilder>>
|
||||
====
|
||||
|
||||
This will ensure any URL that has a host of "localhost" will be directed at our MockMvc
|
||||
instance without the need for HTTP. Any other URL will be requested as normal. This allows
|
||||
for easily testing with the use of CDNs.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-webdriver-usage]]
|
||||
====== MockMvc and WebDriver Usage
|
||||
|
||||
Now we can use WebDriver as we normally would, but without the need to deploy our
|
||||
application. For example, we can request the view to create a message with the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
CreateMessagePage page = CreateMessagePage.to(driver);
|
||||
----
|
||||
|
||||
We can then fill out the form and submit it to create a message.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
ViewMessagePage viewMessagePage =
|
||||
page.createMessage(ViewMessagePage.class, expectedSummary, expectedText);
|
||||
----
|
||||
|
||||
This improves on the design of our
|
||||
<<spring-mvc-test-server-htmlunit-mah-usage,HtmlUnit test>> by leveraging the Page Object
|
||||
Pattern. As we mentioned in <<spring-mvc-test-server-htmlunit-webdriver-why>>, we could
|
||||
use the Page Object Pattern with HtmlUnit, but it is much easier now. Let's take a look at
|
||||
our `CreateMessagePage`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public class CreateMessagePage
|
||||
extends AbstractPage { // <1>
|
||||
|
||||
// <2>
|
||||
private WebElement summary;
|
||||
private WebElement text;
|
||||
|
||||
// <3>
|
||||
@FindBy(css = "input[type=submit]")
|
||||
private WebElement submit;
|
||||
|
||||
public CreateMessagePage(WebDriver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public <T> T createMessage(Class<T> resultPage, String summary, String details) {
|
||||
this.summary.sendKeys(summary);
|
||||
this.text.sendKeys(details);
|
||||
this.submit.click();
|
||||
return PageFactory.initElements(driver, resultPage);
|
||||
}
|
||||
|
||||
public static CreateMessagePage to(WebDriver driver) {
|
||||
driver.get("http://localhost:9990/mail/messages/form");
|
||||
return PageFactory.initElements(driver, CreateMessagePage.class);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
<1> The first thing you will notice is that our `CreateMessagePage` extends the
|
||||
`AbstractPage`. We won't go over the details of `AbstractPage`, but in summary it contains
|
||||
all the common functionality of all our pages. For example, if your application has a
|
||||
navigational bar, global error messages, etc. This logic can be placed in a shared
|
||||
location.
|
||||
|
||||
<2> The next thing you will find is that we have a member variable for each of the parts
|
||||
of the HTML, `WebElement`, we are interested in. ``WebDriver``'s
|
||||
https://code.google.com/p/selenium/wiki/PageFactory[PageFactory] allows us to remove a lot
|
||||
of code from HtmlUnit version of `CreateMessagePage` by automatically resolving each
|
||||
`WebElement`. The
|
||||
http://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[PageFactory#initElements(WebDriver,Class<T>)]
|
||||
method will automatically resolve each `WebElement` by using the field name and trying to
|
||||
look it up by id or name of the element on the HTML page.
|
||||
|
||||
<3> We can use the
|
||||
https://code.google.com/p/selenium/wiki/PageFactory#Making_the_Example_Work_Using_Annotations[@FindBy annotation]
|
||||
to override the default. Our example demonstrates how we can use the `@FindBy` annotation
|
||||
to lookup our submit button using the css selector of *input[type=submit]*.
|
||||
|
||||
Finally, we can verify that a new message was created successfully
|
||||
|
||||
[source,java]
|
||||
----
|
||||
assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
|
||||
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
|
||||
----
|
||||
|
||||
We can see that our `ViewMessagePage` can allow us to interact with our custom domain
|
||||
model. For example, it exposes a method that returns a `Message` object.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public Message getMessage() throws ParseException {
|
||||
Message message = new Message();
|
||||
message.setId(getId());
|
||||
message.setCreated(getCreated());
|
||||
message.setSummary(getSummary());
|
||||
message.setText(getText());
|
||||
return message;
|
||||
}
|
||||
----
|
||||
|
||||
We can then leverage the rich domain objects in our assertions.
|
||||
|
||||
Last, don't forget to close the `WebDriver` instance when we are done.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@After
|
||||
public void destroy() {
|
||||
if(driver != null) {
|
||||
driver.close();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
For additional information on using WebDriver, refer to the
|
||||
https://code.google.com/p/selenium/wiki/GettingStarted[WebDriver documentation].
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-webdriver-advanced-builder]]
|
||||
====== Advanced MockMvcHtmlUnitDriverBuilder
|
||||
|
||||
In our example above we used `MockMvcHtmlUnitDriverBuilder` in the simplest way possible.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.webAppContextSetup(context)
|
||||
.createWebClient();
|
||||
}
|
||||
----
|
||||
|
||||
We could also specify some optional arguments:
|
||||
|
||||
|
||||
[source,java]
|
||||
----
|
||||
WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
// demonstrates applying a MockMvcConfigurer (Spring Security)
|
||||
.webAppContextSetup(context, springSecurity())
|
||||
// for illustration only - defaults to ""
|
||||
.contextPath("")
|
||||
// By default MockMvc is used for localhost only
|
||||
// the following will use MockMvc for example.com and example.org too
|
||||
.useMockMvcForHosts("example.com","example.org")
|
||||
.createWebClient();
|
||||
}
|
||||
----
|
||||
|
||||
We could also perform the exact same setup using the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
MockMvc mockMvc = MockMvcBuilders
|
||||
.webAppContextSetup(context)
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
// for illustration only - defaults to ""
|
||||
.contextPath("")
|
||||
// By default MockMvc is used for localhost only
|
||||
// the following will use MockMvc for example.com and example.org too
|
||||
.useMockMvcForHosts("example.com","example.org")
|
||||
.createWebClient();
|
||||
----
|
||||
|
||||
This is more verbose, but by building the `WebDriver` with a `MockMvc` instance we have
|
||||
the full power of `MockMvc` at our finger tips. Ultimately, this is simply performing the
|
||||
following:
|
||||
|
||||
[TIP]
|
||||
====
|
||||
For additional information on creating a `MockMvc` instance refer to
|
||||
<<spring-mvc-test-server-setup-options>>.
|
||||
====
|
||||
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-geb]]
|
||||
===== MockMvc and Geb
|
||||
|
||||
In the previous section, we saw how to use MockMvc with WebDriver.
|
||||
In this section, we will use http://www.gebish.org/[Geb] to make our tests more Groovy.
|
||||
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-geb-why]]
|
||||
====== Why Geb and MockMvc?
|
||||
|
||||
Geb is backed by WebDriver, so it offers many of the
|
||||
<<spring-mvc-test-server-htmlunit-webdriver-why,same benefits>> we got from WebDriver.
|
||||
However, Geb makes things even easier by taking care of some of the boiler plate code for
|
||||
us.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-geb-setup]]
|
||||
====== MockMvc and Geb Setup
|
||||
|
||||
We can easily initialize Geb with a WebDriver implementation that uses `MockMvc` with the
|
||||
following:
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
def setup() {
|
||||
browser.driver = MockMvcHtmlUnitDriverBuilder
|
||||
.webAppContextSetup(context, springSecurity())
|
||||
.createDriver()
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
This is a simple example of using `MockMvcHtmlUnitDriverBuilder`.
|
||||
For more advanced usage, refer to <<Advanced MockMvcHtmlUnitDriverBuilder>>
|
||||
====
|
||||
|
||||
This will ensure any URL that has a host of "localhost" will be directed at our MockMvc
|
||||
instance without the need for HTTP. Any other URL will be requested as normal. This allows
|
||||
for easily testing with the use of CDNs.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-geb-usage]]
|
||||
====== MockMvc and Geb Usage
|
||||
|
||||
Now we can use Geb as we normally would, but without the need to deploy our application.
|
||||
For example, we can request the view to create a message with the following:
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
to CreateMessagePage
|
||||
----
|
||||
|
||||
We can then fill out the form and submit it to create a message.
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
when:
|
||||
form.summary = expectedSummary
|
||||
form.text = expectedMessage
|
||||
submit.click(ViewMessagePage)
|
||||
----
|
||||
|
||||
Any unrecognized method calls or property accesses/references that are not found will be
|
||||
forwarded to the current page object. This removes a lot of the boilerplate code we needed
|
||||
when using WebDriver directly.
|
||||
|
||||
Additionally, this improves on the design of our
|
||||
<<spring-mvc-test-server-htmlunit-mah-usage,HtmlUnit test>>. The most obvious change is
|
||||
that we are now using the Page Object Pattern. As we mentioned in
|
||||
<<spring-mvc-test-server-htmlunit-webdriver-why>>, we could use the Page Object Pattern
|
||||
with HtmlUnit, but it is much easier now.
|
||||
|
||||
Let's take a look at our `CreateMessagePage`.
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
class CreateMessagePage extends Page {
|
||||
static at = { assert title == 'Messages : Create'; true }
|
||||
static url = 'messages/form'
|
||||
static content = {
|
||||
submit { $('input[type=submit]') }
|
||||
form { $('form') }
|
||||
errors(required:false) { $('label.error, .alert-error')?.text() }
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The first thing you will notice is that our `CreateMessagePage` extends the `Page`.
|
||||
We won't go over the details of `Page`, but in summary it contains base functionality for all our pages.
|
||||
|
||||
The next thing you will notice is that we define a URL in which this page can be found.
|
||||
This allows us to navigate to the page with:
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
to CreateMessagePage
|
||||
----
|
||||
|
||||
We also have a closure that determines if we are at the specified page.
|
||||
It should return true if we are on the correct page.
|
||||
This is why we can assert that we are on the correct page with:
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
then:
|
||||
at CreateMessagePage
|
||||
errors.contains('This field is required.')
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
We use an assertion in the closure, so we can determine where things went wrong if we were
|
||||
at the wrong page.
|
||||
====
|
||||
|
||||
We last create a content closure that specifies all the areas of interest within the page.
|
||||
We can use a
|
||||
http://www.gebish.org/manual/current/intro.html#the_jquery_ish_navigator_api[jQuery-ish Navigator API]
|
||||
to select the content we are interested in.
|
||||
|
||||
Finally, we can verify that a new message was created successfully
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
then:
|
||||
at ViewMessagePage
|
||||
success == 'Successfully created a new message'
|
||||
id
|
||||
date
|
||||
summary == expectedSummary
|
||||
message == expectedMessage
|
||||
----
|
||||
|
||||
|
||||
[[spring-mvc-test-client]]
|
||||
==== Client-Side REST Tests
|
||||
Client-side tests are for code using the `RestTemplate`. The goal is to define expected
|
||||
|
||||
Reference in New Issue
Block a user