Add support for building a request directly from MockMvcTester
Closes gh-32913'
This commit is contained in:
commit
bad4e18b4d
|
@ -23,13 +23,18 @@ import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import org.assertj.core.api.AssertProvider;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.converter.GenericHttpMessageConverter;
|
import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.RequestBuilder;
|
import org.springframework.test.web.servlet.RequestBuilder;
|
||||||
|
import org.springframework.test.web.servlet.request.AbstractMockHttpServletRequestBuilder;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
|
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
@ -58,11 +63,25 @@ import org.springframework.web.context.WebApplicationContext;
|
||||||
* MockMvcTester mvc = MockMvcTester.of(new PersonController());
|
* MockMvcTester mvc = MockMvcTester.of(new PersonController());
|
||||||
* </code></pre>
|
* </code></pre>
|
||||||
*
|
*
|
||||||
* <p>Once a tester instance is available, you can perform requests in a similar
|
* <p>Simple, single-statement assertions can be done wrapping the request
|
||||||
* fashion as with {@link MockMvc}, and wrapping the result in
|
* builder in {@code assertThat()} provides access to assertions. For instance:
|
||||||
* {@code assertThat()} provides access to assertions. For instance:
|
|
||||||
* <pre><code class="java">
|
* <pre><code class="java">
|
||||||
* // perform a GET on /hi and assert the response body is equal to Hello
|
* // perform a GET on /hi and assert the response body is equal to Hello
|
||||||
|
* assertThat(mvc.get().uri("/hi")).hasStatusOk().hasBodyTextEqualTo("Hello");
|
||||||
|
* </code></pre>
|
||||||
|
*
|
||||||
|
*<p>For more complex scenarios the {@linkplain MvcTestResult result} of the
|
||||||
|
* exchange can be assigned in a variable to run multiple assertions:
|
||||||
|
* <pre><code class="java">
|
||||||
|
* // perform a POST on /save and assert the response body is empty
|
||||||
|
* MvcTestResult result = mvc.post().uri("/save").exchange();
|
||||||
|
* assertThat(result).hasStatus(HttpStatus.CREATED);
|
||||||
|
* assertThat(result).body().isEmpty();
|
||||||
|
* </code></pre>
|
||||||
|
*
|
||||||
|
* <p>You can also perform requests using the static builders approach that
|
||||||
|
* {@link MockMvc} uses. For instance:<pre><code class="java">
|
||||||
|
* // perform a GET on /hi and assert the response body is equal to Hello
|
||||||
* assertThat(mvc.perform(get("/hi")))
|
* assertThat(mvc.perform(get("/hi")))
|
||||||
* .hasStatusOk().hasBodyTextEqualTo("Hello");
|
* .hasStatusOk().hasBodyTextEqualTo("Hello");
|
||||||
* </code></pre>
|
* </code></pre>
|
||||||
|
@ -74,12 +93,11 @@ import org.springframework.web.context.WebApplicationContext;
|
||||||
* which allows you to assert that a request failed unexpectedly:
|
* which allows you to assert that a request failed unexpectedly:
|
||||||
* <pre><code class="java">
|
* <pre><code class="java">
|
||||||
* // perform a GET on /boom and assert the message for the the unresolved exception
|
* // perform a GET on /boom and assert the message for the the unresolved exception
|
||||||
* assertThat(mvc.perform(get("/boom")))
|
* assertThat(mvc.get().uri("/boom")).hasUnresolvedException())
|
||||||
* .hasUnresolvedException())
|
|
||||||
* .withMessage("Test exception");
|
* .withMessage("Test exception");
|
||||||
* </code></pre>
|
* </code></pre>
|
||||||
*
|
*
|
||||||
* <p>{@link MockMvcTester} can be configured with a list of
|
* <p>{@code MockMvcTester} can be configured with a list of
|
||||||
* {@linkplain HttpMessageConverter message converters} to allow the response
|
* {@linkplain HttpMessageConverter message converters} to allow the response
|
||||||
* body to be deserialized, rather than asserting on the raw values.
|
* body to be deserialized, rather than asserting on the raw values.
|
||||||
*
|
*
|
||||||
|
@ -104,8 +122,7 @@ public final class MockMvcTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link MockMvcTester} instance that delegates to the given
|
* Create an instance that delegates to the given {@link MockMvc} instance.
|
||||||
* {@link MockMvc} instance.
|
|
||||||
* @param mockMvc the MockMvc instance to delegate calls to
|
* @param mockMvc the MockMvc instance to delegate calls to
|
||||||
*/
|
*/
|
||||||
public static MockMvcTester create(MockMvc mockMvc) {
|
public static MockMvcTester create(MockMvc mockMvc) {
|
||||||
|
@ -113,9 +130,9 @@ public final class MockMvcTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an {@link MockMvcTester} instance using the given, fully
|
* Create an instance using the given, fully initialized (i.e.,
|
||||||
* initialized (i.e., <em>refreshed</em>) {@link WebApplicationContext}. The
|
* <em>refreshed</em>) {@link WebApplicationContext}. The given
|
||||||
* given {@code customizations} are applied to the {@link DefaultMockMvcBuilder}
|
* {@code customizations} are applied to the {@link DefaultMockMvcBuilder}
|
||||||
* that ultimately creates the underlying {@link MockMvc} instance.
|
* that ultimately creates the underlying {@link MockMvc} instance.
|
||||||
* <p>If no further customization of the underlying {@link MockMvc} instance
|
* <p>If no further customization of the underlying {@link MockMvc} instance
|
||||||
* is required, use {@link #from(WebApplicationContext)}.
|
* is required, use {@link #from(WebApplicationContext)}.
|
||||||
|
@ -134,8 +151,8 @@ public final class MockMvcTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut to create an {@link MockMvcTester} instance using the given,
|
* Shortcut to create an instance using the given fully initialized (i.e.,
|
||||||
* fully initialized (i.e., <em>refreshed</em>) {@link WebApplicationContext}.
|
* <em>refreshed</em>) {@link WebApplicationContext}.
|
||||||
* <p>Consider using {@link #from(WebApplicationContext, Function)} if
|
* <p>Consider using {@link #from(WebApplicationContext, Function)} if
|
||||||
* further customization of the underlying {@link MockMvc} instance is
|
* further customization of the underlying {@link MockMvc} instance is
|
||||||
* required.
|
* required.
|
||||||
|
@ -148,9 +165,8 @@ public final class MockMvcTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an {@link MockMvcTester} instance by registering one or more
|
* Create an instance by registering one or more {@code @Controller} instances
|
||||||
* {@code @Controller} instances and configuring Spring MVC infrastructure
|
* and configuring Spring MVC infrastructure programmatically.
|
||||||
* programmatically.
|
|
||||||
* <p>This allows full control over the instantiation and initialization of
|
* <p>This allows full control over the instantiation and initialization of
|
||||||
* controllers and their dependencies, similar to plain unit tests while
|
* controllers and their dependencies, similar to plain unit tests while
|
||||||
* also making it possible to test one controller at a time.
|
* also making it possible to test one controller at a time.
|
||||||
|
@ -170,8 +186,8 @@ public final class MockMvcTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut to create an {@link MockMvcTester} instance by registering one
|
* Shortcut to create an instance by registering one or more {@code @Controller}
|
||||||
* or more {@code @Controller} instances.
|
* instances.
|
||||||
* <p>The minimum infrastructure required by the
|
* <p>The minimum infrastructure required by the
|
||||||
* {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet}
|
* {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet}
|
||||||
* to serve requests with annotated controllers is created. Consider using
|
* to serve requests with annotated controllers is created. Consider using
|
||||||
|
@ -187,8 +203,8 @@ public final class MockMvcTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a new {@link MockMvcTester} instance using the specified
|
* Return a new instance using the specified {@linkplain HttpMessageConverter
|
||||||
* {@linkplain HttpMessageConverter message converters}.
|
* message converters}.
|
||||||
* <p>If none are specified, only basic assertions on the response body can
|
* <p>If none are specified, only basic assertions on the response body can
|
||||||
* be performed. Consider registering a suitable JSON converter for asserting
|
* be performed. Consider registering a suitable JSON converter for asserting
|
||||||
* against JSON data structures.
|
* against JSON data structures.
|
||||||
|
@ -200,8 +216,105 @@ public final class MockMvcTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a request and return a {@link MvcTestResult result} that can be
|
* Prepare an HTTP GET request.
|
||||||
* used with standard {@link org.assertj.core.api.Assertions AssertJ} assertions.
|
* <p>The returned builder can be wrapped in {@code assertThat} to enable
|
||||||
|
* assertions on the result. For multi-statements assertions, use
|
||||||
|
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
|
||||||
|
* result.
|
||||||
|
* @return a request builder for specifying the target URI
|
||||||
|
*/
|
||||||
|
public MockMvcRequestBuilder get() {
|
||||||
|
return method(HttpMethod.GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare an HTTP HEAD request.
|
||||||
|
* <p>The returned builder can be wrapped in {@code assertThat} to enable
|
||||||
|
* assertions on the result. For multi-statements assertions, use
|
||||||
|
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
|
||||||
|
* result.
|
||||||
|
* @return a request builder for specifying the target URI
|
||||||
|
*/
|
||||||
|
public MockMvcRequestBuilder head() {
|
||||||
|
return method(HttpMethod.HEAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare an HTTP POST request.
|
||||||
|
* <p>The returned builder can be wrapped in {@code assertThat} to enable
|
||||||
|
* assertions on the result. For multi-statements assertions, use
|
||||||
|
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
|
||||||
|
* result.
|
||||||
|
* @return a request builder for specifying the target URI
|
||||||
|
*/
|
||||||
|
public MockMvcRequestBuilder post() {
|
||||||
|
return method(HttpMethod.POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare an HTTP PUT request.
|
||||||
|
* <p>The returned builder can be wrapped in {@code assertThat} to enable
|
||||||
|
* assertions on the result. For multi-statements assertions, use
|
||||||
|
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
|
||||||
|
* result.
|
||||||
|
* @return a request builder for specifying the target URI
|
||||||
|
*/
|
||||||
|
public MockMvcRequestBuilder put() {
|
||||||
|
return method(HttpMethod.PUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare an HTTP PATCH request.
|
||||||
|
* <p>The returned builder can be wrapped in {@code assertThat} to enable
|
||||||
|
* assertions on the result. For multi-statements assertions, use
|
||||||
|
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
|
||||||
|
* result.
|
||||||
|
* @return a request builder for specifying the target URI
|
||||||
|
*/
|
||||||
|
public MockMvcRequestBuilder patch() {
|
||||||
|
return method(HttpMethod.PATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare an HTTP DELETE request.
|
||||||
|
* <p>The returned builder can be wrapped in {@code assertThat} to enable
|
||||||
|
* assertions on the result. For multi-statements assertions, use
|
||||||
|
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
|
||||||
|
* result.
|
||||||
|
* @return a request builder for specifying the target URI
|
||||||
|
*/
|
||||||
|
public MockMvcRequestBuilder delete() {
|
||||||
|
return method(HttpMethod.DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare an HTTP OPTIONS request.
|
||||||
|
* <p>The returned builder can be wrapped in {@code assertThat} to enable
|
||||||
|
* assertions on the result. For multi-statements assertions, use
|
||||||
|
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
|
||||||
|
* result.
|
||||||
|
* @return a request builder for specifying the target URI
|
||||||
|
*/
|
||||||
|
public MockMvcRequestBuilder options() {
|
||||||
|
return method(HttpMethod.OPTIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a request for the specified {@code HttpMethod}.
|
||||||
|
* <p>The returned builder can be wrapped in {@code assertThat} to enable
|
||||||
|
* assertions on the result. For multi-statements assertions, use
|
||||||
|
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
|
||||||
|
* result.
|
||||||
|
* @return a request builder for specifying the target URI
|
||||||
|
*/
|
||||||
|
public MockMvcRequestBuilder method(HttpMethod method) {
|
||||||
|
return new MockMvcRequestBuilder(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a request using {@link MockMvcRequestBuilders} and return a
|
||||||
|
* {@link MvcTestResult result} that can be used with standard
|
||||||
|
* {@link org.assertj.core.api.Assertions AssertJ} assertions.
|
||||||
* <p>Use static methods of {@link MockMvcRequestBuilders} to prepare the
|
* <p>Use static methods of {@link MockMvcRequestBuilders} to prepare the
|
||||||
* request, wrapping the invocation in {@code assertThat}. The following
|
* request, wrapping the invocation in {@code assertThat}. The following
|
||||||
* asserts that a {@linkplain MockMvcRequestBuilders#get(URI) GET} request
|
* asserts that a {@linkplain MockMvcRequestBuilders#get(URI) GET} request
|
||||||
|
@ -226,6 +339,8 @@ public final class MockMvcTester {
|
||||||
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
|
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
|
||||||
* @return an {@link MvcTestResult} to be wrapped in {@code assertThat}
|
* @return an {@link MvcTestResult} to be wrapped in {@code assertThat}
|
||||||
* @see MockMvc#perform(RequestBuilder)
|
* @see MockMvc#perform(RequestBuilder)
|
||||||
|
* @see #get()
|
||||||
|
* @see #post()
|
||||||
*/
|
*/
|
||||||
public MvcTestResult perform(RequestBuilder requestBuilder) {
|
public MvcTestResult perform(RequestBuilder requestBuilder) {
|
||||||
Object result = getMvcResultOrFailure(requestBuilder);
|
Object result = getMvcResultOrFailure(requestBuilder);
|
||||||
|
@ -259,4 +374,25 @@ public final class MockMvcTester {
|
||||||
.findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link MockHttpServletRequest} that supports AssertJ.
|
||||||
|
*/
|
||||||
|
public final class MockMvcRequestBuilder extends AbstractMockHttpServletRequestBuilder<MockMvcRequestBuilder>
|
||||||
|
implements AssertProvider<MvcTestResultAssert> {
|
||||||
|
|
||||||
|
private MockMvcRequestBuilder(HttpMethod httpMethod) {
|
||||||
|
super(httpMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MvcTestResult exchange() {
|
||||||
|
return perform(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MvcTestResultAssert assertThat() {
|
||||||
|
return new MvcTestResultAssert(exchange(), MockMvcTester.this.jsonMessageConverter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ import org.springframework.test.web.reactive.server.MockServerClientHttpResponse
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.RequestBuilder;
|
import org.springframework.test.web.servlet.RequestBuilder;
|
||||||
|
import org.springframework.test.web.servlet.request.AbstractMockHttpServletRequestBuilder;
|
||||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||||
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
|
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
@ -134,7 +135,7 @@ public class MockMvcHttpConnector implements ClientHttpConnector {
|
||||||
// Initialize the client request
|
// Initialize the client request
|
||||||
requestCallback.apply(httpRequest).block(TIMEOUT);
|
requestCallback.apply(httpRequest).block(TIMEOUT);
|
||||||
|
|
||||||
MockHttpServletRequestBuilder requestBuilder =
|
AbstractMockHttpServletRequestBuilder<?> requestBuilder =
|
||||||
initRequestBuilder(httpMethod, uri, httpRequest, contentRef.get());
|
initRequestBuilder(httpMethod, uri, httpRequest, contentRef.get());
|
||||||
|
|
||||||
requestBuilder.headers(httpRequest.getHeaders());
|
requestBuilder.headers(httpRequest.getHeaders());
|
||||||
|
@ -149,7 +150,7 @@ public class MockMvcHttpConnector implements ClientHttpConnector {
|
||||||
return requestBuilder;
|
return requestBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MockHttpServletRequestBuilder initRequestBuilder(
|
private AbstractMockHttpServletRequestBuilder<?> initRequestBuilder(
|
||||||
HttpMethod httpMethod, URI uri, MockClientHttpRequest httpRequest, @Nullable byte[] bytes) {
|
HttpMethod httpMethod, URI uri, MockClientHttpRequest httpRequest, @Nullable byte[] bytes) {
|
||||||
|
|
||||||
String contentType = httpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
|
String contentType = httpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
|
||||||
|
|
|
@ -0,0 +1,948 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2024 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
|
||||||
|
*
|
||||||
|
* https://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.request;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jakarta.servlet.ServletContext;
|
||||||
|
import jakarta.servlet.ServletRequest;
|
||||||
|
import jakarta.servlet.http.Cookie;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
import org.springframework.beans.Mergeable;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpInputMessage;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpOutputMessage;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.FormHttpMessageConverter;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
import org.springframework.web.servlet.FlashMap;
|
||||||
|
import org.springframework.web.servlet.FlashMapManager;
|
||||||
|
import org.springframework.web.servlet.support.SessionFlashMapManager;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
import org.springframework.web.util.UriUtils;
|
||||||
|
import org.springframework.web.util.UrlPathHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base builder for {@link MockHttpServletRequest} required as input to
|
||||||
|
* perform requests in {@link MockMvc}.
|
||||||
|
*
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
* @author Sam Brannen
|
||||||
|
* @author Kamill Sokol
|
||||||
|
* @since 6.2
|
||||||
|
* @param <B> a self reference to the builder type
|
||||||
|
*/
|
||||||
|
public abstract class AbstractMockHttpServletRequestBuilder<B extends AbstractMockHttpServletRequestBuilder<B>>
|
||||||
|
implements ConfigurableSmartRequestBuilder<B>, Mergeable {
|
||||||
|
|
||||||
|
private final HttpMethod method;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private URI uri;
|
||||||
|
|
||||||
|
private String contextPath = "";
|
||||||
|
|
||||||
|
private String servletPath = "";
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String pathInfo = "";
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Boolean secure;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Principal principal;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private MockHttpSession session;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String remoteAddress;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String characterEncoding;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private byte[] content;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String contentType;
|
||||||
|
|
||||||
|
private final MultiValueMap<String, Object> headers = new LinkedMultiValueMap<>();
|
||||||
|
|
||||||
|
private final MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
|
||||||
|
|
||||||
|
private final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
|
|
||||||
|
private final MultiValueMap<String, String> formFields = new LinkedMultiValueMap<>();
|
||||||
|
|
||||||
|
private final List<Cookie> cookies = new ArrayList<>();
|
||||||
|
|
||||||
|
private final List<Locale> locales = new ArrayList<>();
|
||||||
|
|
||||||
|
private final Map<String, Object> requestAttributes = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private final Map<String, Object> sessionAttributes = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private final Map<String, Object> flashAttributes = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private final List<RequestPostProcessor> postProcessors = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance using the specified {@link HttpMethod}.
|
||||||
|
* @param httpMethod the HTTP method (GET, POST, etc.)
|
||||||
|
*/
|
||||||
|
protected AbstractMockHttpServletRequestBuilder(HttpMethod httpMethod) {
|
||||||
|
Assert.notNull(httpMethod, "'httpMethod' is required");
|
||||||
|
this.method = httpMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected B self() {
|
||||||
|
return (B) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the URI using an absolute, fully constructed {@link java.net.URI}.
|
||||||
|
*/
|
||||||
|
public B uri(URI uri) {
|
||||||
|
this.uri = uri;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the URI for the request using a URI template and URI variables.
|
||||||
|
*/
|
||||||
|
public B uri(String uriTemplate, Object... uriVariables) {
|
||||||
|
return uri(initUri(uriTemplate, uriVariables));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URI initUri(String uri, Object[] vars) {
|
||||||
|
Assert.notNull(uri, "'uri' must not be null");
|
||||||
|
Assert.isTrue(uri.isEmpty() || uri.startsWith("/") || uri.startsWith("http://") || uri.startsWith("https://"),
|
||||||
|
() -> "'uri' should start with a path or be a complete HTTP URI: " + uri);
|
||||||
|
String uriString = (uri.isEmpty() ? "/" : uri);
|
||||||
|
return UriComponentsBuilder.fromUriString(uriString).buildAndExpand(vars).encode().toUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the portion of the requestURI that represents the context path.
|
||||||
|
* The context path, if specified, must match to the start of the request URI.
|
||||||
|
* <p>In most cases, tests can be written by omitting the context path from
|
||||||
|
* the requestURI. This is because most applications don't actually depend
|
||||||
|
* on the name under which they're deployed. If specified here, the context
|
||||||
|
* path must start with a "/" and must not end with a "/".
|
||||||
|
* @see jakarta.servlet.http.HttpServletRequest#getContextPath()
|
||||||
|
*/
|
||||||
|
public B contextPath(String contextPath) {
|
||||||
|
if (StringUtils.hasText(contextPath)) {
|
||||||
|
Assert.isTrue(contextPath.startsWith("/"), "Context path must start with a '/'");
|
||||||
|
Assert.isTrue(!contextPath.endsWith("/"), "Context path must not end with a '/'");
|
||||||
|
}
|
||||||
|
this.contextPath = contextPath;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the portion of the requestURI that represents the path to which
|
||||||
|
* the Servlet is mapped. This is typically a portion of the requestURI
|
||||||
|
* after the context path.
|
||||||
|
* <p>In most cases, tests can be written by omitting the servlet path from
|
||||||
|
* the requestURI. This is because most applications don't actually depend
|
||||||
|
* on the prefix to which a servlet is mapped. For example if a Servlet is
|
||||||
|
* mapped to {@code "/main/*"}, tests can be written with the requestURI
|
||||||
|
* {@code "/accounts/1"} as opposed to {@code "/main/accounts/1"}.
|
||||||
|
* If specified here, the servletPath must start with a "/" and must not
|
||||||
|
* end with a "/".
|
||||||
|
* @see jakarta.servlet.http.HttpServletRequest#getServletPath()
|
||||||
|
*/
|
||||||
|
public B servletPath(String servletPath) {
|
||||||
|
if (StringUtils.hasText(servletPath)) {
|
||||||
|
Assert.isTrue(servletPath.startsWith("/"), "Servlet path must start with a '/'");
|
||||||
|
Assert.isTrue(!servletPath.endsWith("/"), "Servlet path must not end with a '/'");
|
||||||
|
}
|
||||||
|
this.servletPath = servletPath;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the portion of the requestURI that represents the pathInfo.
|
||||||
|
* <p>If left unspecified (recommended), the pathInfo will be automatically derived
|
||||||
|
* by removing the contextPath and the servletPath from the requestURI and using any
|
||||||
|
* remaining part. If specified here, the pathInfo must start with a "/".
|
||||||
|
* <p>If specified, the pathInfo will be used as-is.
|
||||||
|
* @see jakarta.servlet.http.HttpServletRequest#getPathInfo()
|
||||||
|
*/
|
||||||
|
public B pathInfo(@Nullable String pathInfo) {
|
||||||
|
if (StringUtils.hasText(pathInfo)) {
|
||||||
|
Assert.isTrue(pathInfo.startsWith("/"), "Path info must start with a '/'");
|
||||||
|
}
|
||||||
|
this.pathInfo = pathInfo;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the secure property of the {@link ServletRequest} indicating use of a
|
||||||
|
* secure channel, such as HTTPS.
|
||||||
|
* @param secure whether the request is using a secure channel
|
||||||
|
*/
|
||||||
|
public B secure(boolean secure){
|
||||||
|
this.secure = secure;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the character encoding of the request.
|
||||||
|
* @param encoding the character encoding
|
||||||
|
* @since 5.3.10
|
||||||
|
* @see StandardCharsets
|
||||||
|
* @see #characterEncoding(String)
|
||||||
|
*/
|
||||||
|
public B characterEncoding(Charset encoding) {
|
||||||
|
return characterEncoding(encoding.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the character encoding of the request.
|
||||||
|
* @param encoding the character encoding
|
||||||
|
*/
|
||||||
|
public B characterEncoding(String encoding) {
|
||||||
|
this.characterEncoding = encoding;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the request body.
|
||||||
|
* <p>If content is provided and {@link #contentType(MediaType)} is set to
|
||||||
|
* {@code application/x-www-form-urlencoded}, the content will be parsed
|
||||||
|
* and used to populate the {@link #param(String, String...) request
|
||||||
|
* parameters} map.
|
||||||
|
* @param content the body content
|
||||||
|
*/
|
||||||
|
public B content(byte[] content) {
|
||||||
|
this.content = content;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the request body as a UTF-8 String.
|
||||||
|
* <p>If content is provided and {@link #contentType(MediaType)} is set to
|
||||||
|
* {@code application/x-www-form-urlencoded}, the content will be parsed
|
||||||
|
* and used to populate the {@link #param(String, String...) request
|
||||||
|
* parameters} map.
|
||||||
|
* @param content the body content
|
||||||
|
*/
|
||||||
|
public B content(String content) {
|
||||||
|
this.content = content.getBytes(StandardCharsets.UTF_8);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the 'Content-Type' header of the request.
|
||||||
|
* <p>If content is provided and {@code contentType} is set to
|
||||||
|
* {@code application/x-www-form-urlencoded}, the content will be parsed
|
||||||
|
* and used to populate the {@link #param(String, String...) request
|
||||||
|
* parameters} map.
|
||||||
|
* @param contentType the content type
|
||||||
|
*/
|
||||||
|
public B contentType(MediaType contentType) {
|
||||||
|
Assert.notNull(contentType, "'contentType' must not be null");
|
||||||
|
this.contentType = contentType.toString();
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the 'Content-Type' header of the request as a raw String value,
|
||||||
|
* possibly not even well-formed (for testing purposes).
|
||||||
|
* @param contentType the content type
|
||||||
|
* @since 4.1.2
|
||||||
|
*/
|
||||||
|
public B contentType(String contentType) {
|
||||||
|
Assert.notNull(contentType, "'contentType' must not be null");
|
||||||
|
this.contentType = contentType;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the 'Accept' header to the given media type(s).
|
||||||
|
* @param mediaTypes one or more media types
|
||||||
|
*/
|
||||||
|
public B accept(MediaType... mediaTypes) {
|
||||||
|
Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty");
|
||||||
|
this.headers.set("Accept", MediaType.toString(Arrays.asList(mediaTypes)));
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@code Accept} header using raw String values, possibly not even
|
||||||
|
* well-formed (for testing purposes).
|
||||||
|
* @param mediaTypes one or more media types; internally joined as
|
||||||
|
* comma-separated String
|
||||||
|
*/
|
||||||
|
public B accept(String... mediaTypes) {
|
||||||
|
Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty");
|
||||||
|
this.headers.set("Accept", String.join(", ", mediaTypes));
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a header to the request. Values are always added.
|
||||||
|
* @param name the header name
|
||||||
|
* @param values one or more header values
|
||||||
|
*/
|
||||||
|
public B header(String name, Object... values) {
|
||||||
|
addToMultiValueMap(this.headers, name, values);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all headers to the request. Values are always added.
|
||||||
|
* @param httpHeaders the headers and values to add
|
||||||
|
*/
|
||||||
|
public B headers(HttpHeaders httpHeaders) {
|
||||||
|
httpHeaders.forEach(this.headers::addAll);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a request parameter to {@link MockHttpServletRequest#getParameterMap()}.
|
||||||
|
* <p>In the Servlet API, a request parameter may be parsed from the query
|
||||||
|
* string and/or from the body of an {@code application/x-www-form-urlencoded}
|
||||||
|
* request. This method simply adds to the request parameter map. You may
|
||||||
|
* also use add Servlet request parameters by specifying the query or form
|
||||||
|
* data through one of the following:
|
||||||
|
* <ul>
|
||||||
|
* <li>Supply a URL with a query to {@link MockMvcRequestBuilders}.
|
||||||
|
* <li>Add query params via {@link #queryParam} or {@link #queryParams}.
|
||||||
|
* <li>Provide {@link #content} with {@link #contentType}
|
||||||
|
* {@code application/x-www-form-urlencoded}.
|
||||||
|
* </ul>
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param values one or more values
|
||||||
|
*/
|
||||||
|
public B param(String name, String... values) {
|
||||||
|
addToMultiValueMap(this.parameters, name, values);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variant of {@link #param(String, String...)} with a {@link MultiValueMap}.
|
||||||
|
* @param params the parameters to add
|
||||||
|
* @since 4.2.4
|
||||||
|
*/
|
||||||
|
public B params(MultiValueMap<String, String> params) {
|
||||||
|
params.forEach((name, values) -> {
|
||||||
|
for (String value : values) {
|
||||||
|
this.parameters.add(name, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append to the query string and also add to the
|
||||||
|
* {@link #param(String, String...) request parameters} map. The parameter
|
||||||
|
* name and value are encoded when they are added to the query string.
|
||||||
|
* @param name the parameter name
|
||||||
|
* @param values one or more values
|
||||||
|
* @since 5.2.2
|
||||||
|
*/
|
||||||
|
public B queryParam(String name, String... values) {
|
||||||
|
param(name, values);
|
||||||
|
this.queryParams.addAll(name, Arrays.asList(values));
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append to the query string and also add to the
|
||||||
|
* {@link #params(MultiValueMap) request parameters} map. The parameter
|
||||||
|
* name and value are encoded when they are added to the query string.
|
||||||
|
* @param params the parameters to add
|
||||||
|
* @since 5.2.2
|
||||||
|
*/
|
||||||
|
public B queryParams(MultiValueMap<String, String> params) {
|
||||||
|
params(params);
|
||||||
|
this.queryParams.addAll(params);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the given value(s) to the given form field and also add them to the
|
||||||
|
* {@linkplain #param(String, String...) request parameters} map.
|
||||||
|
* @param name the field name
|
||||||
|
* @param values one or more values
|
||||||
|
* @since 6.1.7
|
||||||
|
*/
|
||||||
|
public B formField(String name, String... values) {
|
||||||
|
param(name, values);
|
||||||
|
this.formFields.addAll(name, Arrays.asList(values));
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variant of {@link #formField(String, String...)} with a {@link MultiValueMap}.
|
||||||
|
* @param formFields the form fields to add
|
||||||
|
* @since 6.1.7
|
||||||
|
*/
|
||||||
|
public B formFields(MultiValueMap<String, String> formFields) {
|
||||||
|
params(formFields);
|
||||||
|
this.formFields.addAll(formFields);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the given cookies to the request. Cookies are always added.
|
||||||
|
* @param cookies the cookies to add
|
||||||
|
*/
|
||||||
|
public B cookie(Cookie... cookies) {
|
||||||
|
Assert.notEmpty(cookies, "'cookies' must not be empty");
|
||||||
|
this.cookies.addAll(Arrays.asList(cookies));
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the specified locales as preferred request locales.
|
||||||
|
* @param locales the locales to add
|
||||||
|
* @since 4.3.6
|
||||||
|
* @see #locale(Locale)
|
||||||
|
*/
|
||||||
|
public B locale(Locale... locales) {
|
||||||
|
Assert.notEmpty(locales, "'locales' must not be empty");
|
||||||
|
this.locales.addAll(Arrays.asList(locales));
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the locale of the request, overriding any previous locales.
|
||||||
|
* @param locale the locale, or {@code null} to reset it
|
||||||
|
* @see #locale(Locale...)
|
||||||
|
*/
|
||||||
|
public B locale(@Nullable Locale locale) {
|
||||||
|
this.locales.clear();
|
||||||
|
if (locale != null) {
|
||||||
|
this.locales.add(locale);
|
||||||
|
}
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a request attribute.
|
||||||
|
* @param name the attribute name
|
||||||
|
* @param value the attribute value
|
||||||
|
*/
|
||||||
|
public B requestAttr(String name, Object value) {
|
||||||
|
addToMap(this.requestAttributes, name, value);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a session attribute.
|
||||||
|
* @param name the session attribute name
|
||||||
|
* @param value the session attribute value
|
||||||
|
*/
|
||||||
|
public B sessionAttr(String name, Object value) {
|
||||||
|
addToMap(this.sessionAttributes, name, value);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set session attributes.
|
||||||
|
* @param sessionAttributes the session attributes
|
||||||
|
*/
|
||||||
|
public B sessionAttrs(Map<String, Object> sessionAttributes) {
|
||||||
|
Assert.notEmpty(sessionAttributes, "'sessionAttributes' must not be empty");
|
||||||
|
sessionAttributes.forEach(this::sessionAttr);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an "input" flash attribute.
|
||||||
|
* @param name the flash attribute name
|
||||||
|
* @param value the flash attribute value
|
||||||
|
*/
|
||||||
|
public B flashAttr(String name, Object value) {
|
||||||
|
addToMap(this.flashAttributes, name, value);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set flash attributes.
|
||||||
|
* @param flashAttributes the flash attributes
|
||||||
|
*/
|
||||||
|
public B flashAttrs(Map<String, Object> flashAttributes) {
|
||||||
|
Assert.notEmpty(flashAttributes, "'flashAttributes' must not be empty");
|
||||||
|
flashAttributes.forEach(this::flashAttr);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the HTTP session to use, possibly re-used across requests.
|
||||||
|
* <p>Individual attributes provided via {@link #sessionAttr(String, Object)}
|
||||||
|
* override the content of the session provided here.
|
||||||
|
* @param session the HTTP session
|
||||||
|
*/
|
||||||
|
public B session(MockHttpSession session) {
|
||||||
|
Assert.notNull(session, "'session' must not be null");
|
||||||
|
this.session = session;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the principal of the request.
|
||||||
|
* @param principal the principal
|
||||||
|
*/
|
||||||
|
public B principal(Principal principal) {
|
||||||
|
Assert.notNull(principal, "'principal' must not be null");
|
||||||
|
this.principal = principal;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the remote address of the request.
|
||||||
|
* @param remoteAddress the remote address (IP)
|
||||||
|
* @since 6.0.10
|
||||||
|
*/
|
||||||
|
public B remoteAddress(String remoteAddress) {
|
||||||
|
Assert.hasText(remoteAddress, "'remoteAddress' must not be null or blank");
|
||||||
|
this.remoteAddress = remoteAddress;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension point for further initialization of {@link MockHttpServletRequest}
|
||||||
|
* in ways not built directly into the {@code MockHttpServletRequestBuilder}.
|
||||||
|
* Implementation of this interface can have builder-style methods themselves
|
||||||
|
* and be made accessible through static factory methods.
|
||||||
|
* @param postProcessor a post-processor to add
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public B with(RequestPostProcessor postProcessor) {
|
||||||
|
Assert.notNull(postProcessor, "postProcessor is required");
|
||||||
|
this.postProcessors.add(postProcessor);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* @return always returns {@code true}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isMergeEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges the properties of the "parent" RequestBuilder accepting values
|
||||||
|
* only if not already set in "this" instance.
|
||||||
|
* @param parent the parent {@code RequestBuilder} to inherit properties from
|
||||||
|
* @return the result of the merge
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object merge(@Nullable Object parent) {
|
||||||
|
if (parent == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!(parent instanceof AbstractMockHttpServletRequestBuilder<?> parentBuilder)) {
|
||||||
|
throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(this.contextPath)) {
|
||||||
|
this.contextPath = parentBuilder.contextPath;
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(this.servletPath)) {
|
||||||
|
this.servletPath = parentBuilder.servletPath;
|
||||||
|
}
|
||||||
|
if ("".equals(this.pathInfo)) {
|
||||||
|
this.pathInfo = parentBuilder.pathInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.secure == null) {
|
||||||
|
this.secure = parentBuilder.secure;
|
||||||
|
}
|
||||||
|
if (this.principal == null) {
|
||||||
|
this.principal = parentBuilder.principal;
|
||||||
|
}
|
||||||
|
if (this.session == null) {
|
||||||
|
this.session = parentBuilder.session;
|
||||||
|
}
|
||||||
|
if (this.remoteAddress == null) {
|
||||||
|
this.remoteAddress = parentBuilder.remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.characterEncoding == null) {
|
||||||
|
this.characterEncoding = parentBuilder.characterEncoding;
|
||||||
|
}
|
||||||
|
if (this.content == null) {
|
||||||
|
this.content = parentBuilder.content;
|
||||||
|
}
|
||||||
|
if (this.contentType == null) {
|
||||||
|
this.contentType = parentBuilder.contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, List<Object>> entry : parentBuilder.headers.entrySet()) {
|
||||||
|
String headerName = entry.getKey();
|
||||||
|
if (!this.headers.containsKey(headerName)) {
|
||||||
|
this.headers.put(headerName, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, List<String>> entry : parentBuilder.parameters.entrySet()) {
|
||||||
|
String paramName = entry.getKey();
|
||||||
|
if (!this.parameters.containsKey(paramName)) {
|
||||||
|
this.parameters.put(paramName, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, List<String>> entry : parentBuilder.queryParams.entrySet()) {
|
||||||
|
String paramName = entry.getKey();
|
||||||
|
if (!this.queryParams.containsKey(paramName)) {
|
||||||
|
this.queryParams.put(paramName, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, List<String>> entry : parentBuilder.formFields.entrySet()) {
|
||||||
|
String paramName = entry.getKey();
|
||||||
|
if (!this.formFields.containsKey(paramName)) {
|
||||||
|
this.formFields.put(paramName, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Cookie cookie : parentBuilder.cookies) {
|
||||||
|
if (!containsCookie(cookie)) {
|
||||||
|
this.cookies.add(cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Locale locale : parentBuilder.locales) {
|
||||||
|
if (!this.locales.contains(locale)) {
|
||||||
|
this.locales.add(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : parentBuilder.requestAttributes.entrySet()) {
|
||||||
|
String attributeName = entry.getKey();
|
||||||
|
if (!this.requestAttributes.containsKey(attributeName)) {
|
||||||
|
this.requestAttributes.put(attributeName, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, Object> entry : parentBuilder.sessionAttributes.entrySet()) {
|
||||||
|
String attributeName = entry.getKey();
|
||||||
|
if (!this.sessionAttributes.containsKey(attributeName)) {
|
||||||
|
this.sessionAttributes.put(attributeName, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, Object> entry : parentBuilder.flashAttributes.entrySet()) {
|
||||||
|
String attributeName = entry.getKey();
|
||||||
|
if (!this.flashAttributes.containsKey(attributeName)) {
|
||||||
|
this.flashAttributes.put(attributeName, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.postProcessors.addAll(0, parentBuilder.postProcessors);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsCookie(Cookie cookie) {
|
||||||
|
for (Cookie cookieToCheck : this.cookies) {
|
||||||
|
if (ObjectUtils.nullSafeEquals(cookieToCheck.getName(), cookie.getName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a {@link MockHttpServletRequest}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final MockHttpServletRequest buildRequest(ServletContext servletContext) {
|
||||||
|
Assert.notNull(this.uri, "'uri' is required");
|
||||||
|
MockHttpServletRequest request = createServletRequest(servletContext);
|
||||||
|
|
||||||
|
request.setAsyncSupported(true);
|
||||||
|
request.setMethod(this.method.name());
|
||||||
|
|
||||||
|
String requestUri = this.uri.getRawPath();
|
||||||
|
request.setRequestURI(requestUri);
|
||||||
|
|
||||||
|
if (this.uri.getScheme() != null) {
|
||||||
|
request.setScheme(this.uri.getScheme());
|
||||||
|
}
|
||||||
|
if (this.uri.getHost() != null) {
|
||||||
|
request.setServerName(this.uri.getHost());
|
||||||
|
}
|
||||||
|
if (this.uri.getPort() != -1) {
|
||||||
|
request.setServerPort(this.uri.getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePathRequestProperties(request, requestUri);
|
||||||
|
|
||||||
|
if (this.secure != null) {
|
||||||
|
request.setSecure(this.secure);
|
||||||
|
}
|
||||||
|
if (this.principal != null) {
|
||||||
|
request.setUserPrincipal(this.principal);
|
||||||
|
}
|
||||||
|
if (this.remoteAddress != null) {
|
||||||
|
request.setRemoteAddr(this.remoteAddress);
|
||||||
|
}
|
||||||
|
if (this.session != null) {
|
||||||
|
request.setSession(this.session);
|
||||||
|
}
|
||||||
|
|
||||||
|
request.setCharacterEncoding(this.characterEncoding);
|
||||||
|
request.setContent(this.content);
|
||||||
|
request.setContentType(this.contentType);
|
||||||
|
|
||||||
|
this.headers.forEach((name, values) -> {
|
||||||
|
for (Object value : values) {
|
||||||
|
request.addHeader(name, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ObjectUtils.isEmpty(this.content) &&
|
||||||
|
!this.headers.containsKey(HttpHeaders.CONTENT_LENGTH) &&
|
||||||
|
!this.headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
|
||||||
|
|
||||||
|
request.addHeader(HttpHeaders.CONTENT_LENGTH, this.content.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
String query = this.uri.getRawQuery();
|
||||||
|
if (!this.queryParams.isEmpty()) {
|
||||||
|
String str = UriComponentsBuilder.newInstance().queryParams(this.queryParams).build().encode().getQuery();
|
||||||
|
query = StringUtils.hasLength(query) ? (query + "&" + str) : str;
|
||||||
|
}
|
||||||
|
if (query != null) {
|
||||||
|
request.setQueryString(query);
|
||||||
|
}
|
||||||
|
addRequestParams(request, UriComponentsBuilder.fromUri(this.uri).build().getQueryParams());
|
||||||
|
|
||||||
|
this.parameters.forEach((name, values) -> {
|
||||||
|
for (String value : values) {
|
||||||
|
request.addParameter(name, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.formFields.isEmpty()) {
|
||||||
|
if (this.content != null && this.content.length > 0) {
|
||||||
|
throw new IllegalStateException("Could not write form data with an existing body");
|
||||||
|
}
|
||||||
|
Charset charset = (this.characterEncoding != null ?
|
||||||
|
Charset.forName(this.characterEncoding) : StandardCharsets.UTF_8);
|
||||||
|
MediaType mediaType = (request.getContentType() != null ?
|
||||||
|
MediaType.parseMediaType(request.getContentType()) :
|
||||||
|
new MediaType(MediaType.APPLICATION_FORM_URLENCODED, charset));
|
||||||
|
if (!mediaType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)) {
|
||||||
|
throw new IllegalStateException("Invalid content type: '" + mediaType +
|
||||||
|
"' is not compatible with '" + MediaType.APPLICATION_FORM_URLENCODED + "'");
|
||||||
|
}
|
||||||
|
request.setContent(writeFormData(mediaType, charset));
|
||||||
|
if (request.getContentType() == null) {
|
||||||
|
request.setContentType(mediaType.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.content != null && this.content.length > 0) {
|
||||||
|
String requestContentType = request.getContentType();
|
||||||
|
if (requestContentType != null) {
|
||||||
|
try {
|
||||||
|
MediaType mediaType = MediaType.parseMediaType(requestContentType);
|
||||||
|
if (MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)) {
|
||||||
|
addRequestParams(request, parseFormData(mediaType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
// Must be invalid, ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ObjectUtils.isEmpty(this.cookies)) {
|
||||||
|
request.setCookies(this.cookies.toArray(new Cookie[0]));
|
||||||
|
}
|
||||||
|
if (!ObjectUtils.isEmpty(this.locales)) {
|
||||||
|
request.setPreferredLocales(this.locales);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requestAttributes.forEach(request::setAttribute);
|
||||||
|
this.sessionAttributes.forEach((name, attribute) -> {
|
||||||
|
HttpSession session = request.getSession();
|
||||||
|
Assert.state(session != null, "No HttpSession");
|
||||||
|
session.setAttribute(name, attribute);
|
||||||
|
});
|
||||||
|
|
||||||
|
FlashMap flashMap = new FlashMap();
|
||||||
|
flashMap.putAll(this.flashAttributes);
|
||||||
|
FlashMapManager flashMapManager = getFlashMapManager(request);
|
||||||
|
flashMapManager.saveOutputFlashMap(flashMap, request, new MockHttpServletResponse());
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link MockHttpServletRequest} based on the supplied
|
||||||
|
* {@code ServletContext}.
|
||||||
|
* <p>Can be overridden in subclasses.
|
||||||
|
*/
|
||||||
|
protected MockHttpServletRequest createServletRequest(ServletContext servletContext) {
|
||||||
|
return new MockHttpServletRequest(servletContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the contextPath, servletPath, and pathInfo of the request.
|
||||||
|
*/
|
||||||
|
private void updatePathRequestProperties(MockHttpServletRequest request, String requestUri) {
|
||||||
|
if (!requestUri.startsWith(this.contextPath)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Request URI [" + requestUri + "] does not start with context path [" + this.contextPath + "]");
|
||||||
|
}
|
||||||
|
request.setContextPath(this.contextPath);
|
||||||
|
request.setServletPath(this.servletPath);
|
||||||
|
|
||||||
|
if ("".equals(this.pathInfo)) {
|
||||||
|
if (!requestUri.startsWith(this.contextPath + this.servletPath)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid servlet path [" + this.servletPath + "] for request URI [" + requestUri + "]");
|
||||||
|
}
|
||||||
|
String extraPath = requestUri.substring(this.contextPath.length() + this.servletPath.length());
|
||||||
|
this.pathInfo = (StringUtils.hasText(extraPath) ?
|
||||||
|
UrlPathHelper.defaultInstance.decodeRequestString(request, extraPath) : null);
|
||||||
|
}
|
||||||
|
request.setPathInfo(this.pathInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRequestParams(MockHttpServletRequest request, MultiValueMap<String, String> map) {
|
||||||
|
map.forEach((key, values) -> values.forEach(value -> {
|
||||||
|
value = (value != null ? UriUtils.decode(value, StandardCharsets.UTF_8) : null);
|
||||||
|
request.addParameter(UriUtils.decode(key, StandardCharsets.UTF_8), value);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] writeFormData(MediaType mediaType, Charset charset) {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
HttpOutputMessage message = new HttpOutputMessage() {
|
||||||
|
@Override
|
||||||
|
public OutputStream getBody() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(mediaType);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
FormHttpMessageConverter messageConverter = new FormHttpMessageConverter();
|
||||||
|
messageConverter.setCharset(charset);
|
||||||
|
messageConverter.write(this.formFields, mediaType, message);
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new IllegalStateException("Failed to write form data to request body", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private MultiValueMap<String, String> parseFormData(MediaType mediaType) {
|
||||||
|
HttpInputMessage message = new HttpInputMessage() {
|
||||||
|
@Override
|
||||||
|
public InputStream getBody() {
|
||||||
|
byte[] bodyContent = AbstractMockHttpServletRequestBuilder.this.content;
|
||||||
|
return (bodyContent != null ? new ByteArrayInputStream(bodyContent) : InputStream.nullInputStream());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(mediaType);
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (MultiValueMap<String, String>) new FormHttpMessageConverter().read(null, message);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new IllegalStateException("Failed to parse form data in request body", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FlashMapManager getFlashMapManager(MockHttpServletRequest request) {
|
||||||
|
FlashMapManager flashMapManager = null;
|
||||||
|
try {
|
||||||
|
ServletContext servletContext = request.getServletContext();
|
||||||
|
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
|
||||||
|
flashMapManager = wac.getBean(DispatcherServlet.FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
|
||||||
|
}
|
||||||
|
catch (IllegalStateException | NoSuchBeanDefinitionException ex) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return (flashMapManager != null ? flashMapManager : new SessionFlashMapManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
|
||||||
|
for (RequestPostProcessor postProcessor : this.postProcessors) {
|
||||||
|
request = postProcessor.postProcessRequest(request);
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void addToMap(Map<String, Object> map, String name, Object value) {
|
||||||
|
Assert.hasLength(name, "'name' must not be empty");
|
||||||
|
Assert.notNull(value, "'value' must not be null");
|
||||||
|
map.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> void addToMultiValueMap(MultiValueMap<String, T> map, String name, T[] values) {
|
||||||
|
Assert.hasLength(name, "'name' must not be empty");
|
||||||
|
Assert.notEmpty(values, "'values' must not be empty");
|
||||||
|
for (T value : values) {
|
||||||
|
map.add(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,54 +16,12 @@
|
||||||
|
|
||||||
package org.springframework.test.web.servlet.request;
|
package org.springframework.test.web.servlet.request;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletContext;
|
|
||||||
import jakarta.servlet.ServletRequest;
|
|
||||||
import jakarta.servlet.http.Cookie;
|
|
||||||
import jakarta.servlet.http.HttpSession;
|
|
||||||
|
|
||||||
import org.springframework.beans.Mergeable;
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpInputMessage;
|
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpOutputMessage;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.http.converter.FormHttpMessageConverter;
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
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.MockMvc;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
import org.springframework.util.ObjectUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
|
||||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
|
||||||
import org.springframework.web.servlet.FlashMap;
|
|
||||||
import org.springframework.web.servlet.FlashMapManager;
|
|
||||||
import org.springframework.web.servlet.support.SessionFlashMapManager;
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
|
||||||
import org.springframework.web.util.UriUtils;
|
|
||||||
import org.springframework.web.util.UrlPathHelper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default builder for {@link MockHttpServletRequest} required as input to
|
* Default builder for {@link MockHttpServletRequest} required as input to
|
||||||
|
@ -84,60 +42,7 @@ import org.springframework.web.util.UrlPathHelper;
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*/
|
*/
|
||||||
public class MockHttpServletRequestBuilder
|
public class MockHttpServletRequestBuilder
|
||||||
implements ConfigurableSmartRequestBuilder<MockHttpServletRequestBuilder>, Mergeable {
|
extends AbstractMockHttpServletRequestBuilder<MockHttpServletRequestBuilder> {
|
||||||
|
|
||||||
private final HttpMethod method;
|
|
||||||
|
|
||||||
private final URI uri;
|
|
||||||
|
|
||||||
private String contextPath = "";
|
|
||||||
|
|
||||||
private String servletPath = "";
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String pathInfo = "";
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Boolean secure;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Principal principal;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private MockHttpSession session;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String remoteAddress;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String characterEncoding;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private byte[] content;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String contentType;
|
|
||||||
|
|
||||||
private final MultiValueMap<String, Object> headers = new LinkedMultiValueMap<>();
|
|
||||||
|
|
||||||
private final MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
|
|
||||||
|
|
||||||
private final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
|
||||||
|
|
||||||
private final MultiValueMap<String, String> formFields = new LinkedMultiValueMap<>();
|
|
||||||
|
|
||||||
private final List<Cookie> cookies = new ArrayList<>();
|
|
||||||
|
|
||||||
private final List<Locale> locales = new ArrayList<>();
|
|
||||||
|
|
||||||
private final Map<String, Object> requestAttributes = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
private final Map<String, Object> sessionAttributes = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
private final Map<String, Object> flashAttributes = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
private final List<RequestPostProcessor> postProcessors = new ArrayList<>();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Package private constructor. To get an instance, use static factory
|
* Package private constructor. To get an instance, use static factory
|
||||||
|
@ -150,15 +55,8 @@ public class MockHttpServletRequestBuilder
|
||||||
* @param uriVariables zero or more URI variables
|
* @param uriVariables zero or more URI variables
|
||||||
*/
|
*/
|
||||||
MockHttpServletRequestBuilder(HttpMethod httpMethod, String uriTemplate, Object... uriVariables) {
|
MockHttpServletRequestBuilder(HttpMethod httpMethod, String uriTemplate, Object... uriVariables) {
|
||||||
this(httpMethod, initUri(uriTemplate, uriVariables));
|
super(httpMethod);
|
||||||
}
|
super.uri(uriTemplate, uriVariables);
|
||||||
|
|
||||||
private static URI initUri(String uri, Object[] vars) {
|
|
||||||
Assert.notNull(uri, "'uri' must not be null");
|
|
||||||
Assert.isTrue(uri.isEmpty() || uri.startsWith("/") || uri.startsWith("http://") || uri.startsWith("https://"),
|
|
||||||
() -> "'uri' should start with a path or be a complete HTTP URI: " + uri);
|
|
||||||
String uriString = (uri.isEmpty() ? "/" : uri);
|
|
||||||
return UriComponentsBuilder.fromUriString(uriString).buildAndExpand(vars).encode().toUri();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,784 +67,9 @@ public class MockHttpServletRequestBuilder
|
||||||
* @since 4.0.3
|
* @since 4.0.3
|
||||||
*/
|
*/
|
||||||
MockHttpServletRequestBuilder(HttpMethod httpMethod, URI uri) {
|
MockHttpServletRequestBuilder(HttpMethod httpMethod, URI uri) {
|
||||||
Assert.notNull(httpMethod, "'httpMethod' is required");
|
super(httpMethod);
|
||||||
Assert.notNull(uri, "'uri' is required");
|
Assert.notNull(uri, "'uri' is required");
|
||||||
this.method = httpMethod;
|
super.uri(uri);
|
||||||
this.uri = uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the portion of the requestURI that represents the context path.
|
|
||||||
* The context path, if specified, must match to the start of the request URI.
|
|
||||||
* <p>In most cases, tests can be written by omitting the context path from
|
|
||||||
* the requestURI. This is because most applications don't actually depend
|
|
||||||
* on the name under which they're deployed. If specified here, the context
|
|
||||||
* path must start with a "/" and must not end with a "/".
|
|
||||||
* @see jakarta.servlet.http.HttpServletRequest#getContextPath()
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder contextPath(String contextPath) {
|
|
||||||
if (StringUtils.hasText(contextPath)) {
|
|
||||||
Assert.isTrue(contextPath.startsWith("/"), "Context path must start with a '/'");
|
|
||||||
Assert.isTrue(!contextPath.endsWith("/"), "Context path must not end with a '/'");
|
|
||||||
}
|
|
||||||
this.contextPath = contextPath;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the portion of the requestURI that represents the path to which
|
|
||||||
* the Servlet is mapped. This is typically a portion of the requestURI
|
|
||||||
* after the context path.
|
|
||||||
* <p>In most cases, tests can be written by omitting the servlet path from
|
|
||||||
* the requestURI. This is because most applications don't actually depend
|
|
||||||
* on the prefix to which a servlet is mapped. For example if a Servlet is
|
|
||||||
* mapped to {@code "/main/*"}, tests can be written with the requestURI
|
|
||||||
* {@code "/accounts/1"} as opposed to {@code "/main/accounts/1"}.
|
|
||||||
* If specified here, the servletPath must start with a "/" and must not
|
|
||||||
* end with a "/".
|
|
||||||
* @see jakarta.servlet.http.HttpServletRequest#getServletPath()
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder servletPath(String servletPath) {
|
|
||||||
if (StringUtils.hasText(servletPath)) {
|
|
||||||
Assert.isTrue(servletPath.startsWith("/"), "Servlet path must start with a '/'");
|
|
||||||
Assert.isTrue(!servletPath.endsWith("/"), "Servlet path must not end with a '/'");
|
|
||||||
}
|
|
||||||
this.servletPath = servletPath;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the portion of the requestURI that represents the pathInfo.
|
|
||||||
* <p>If left unspecified (recommended), the pathInfo will be automatically derived
|
|
||||||
* by removing the contextPath and the servletPath from the requestURI and using any
|
|
||||||
* remaining part. If specified here, the pathInfo must start with a "/".
|
|
||||||
* <p>If specified, the pathInfo will be used as-is.
|
|
||||||
* @see jakarta.servlet.http.HttpServletRequest#getPathInfo()
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder pathInfo(@Nullable String pathInfo) {
|
|
||||||
if (StringUtils.hasText(pathInfo)) {
|
|
||||||
Assert.isTrue(pathInfo.startsWith("/"), "Path info must start with a '/'");
|
|
||||||
}
|
|
||||||
this.pathInfo = pathInfo;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the secure property of the {@link ServletRequest} indicating use of a
|
|
||||||
* secure channel, such as HTTPS.
|
|
||||||
* @param secure whether the request is using a secure channel
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder secure(boolean secure){
|
|
||||||
this.secure = secure;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the character encoding of the request.
|
|
||||||
* @param encoding the character encoding
|
|
||||||
* @since 5.3.10
|
|
||||||
* @see StandardCharsets
|
|
||||||
* @see #characterEncoding(String)
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder characterEncoding(Charset encoding) {
|
|
||||||
return this.characterEncoding(encoding.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the character encoding of the request.
|
|
||||||
* @param encoding the character encoding
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder characterEncoding(String encoding) {
|
|
||||||
this.characterEncoding = encoding;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the request body.
|
|
||||||
* <p>If content is provided and {@link #contentType(MediaType)} is set to
|
|
||||||
* {@code application/x-www-form-urlencoded}, the content will be parsed
|
|
||||||
* and used to populate the {@link #param(String, String...) request
|
|
||||||
* parameters} map.
|
|
||||||
* @param content the body content
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder content(byte[] content) {
|
|
||||||
this.content = content;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the request body as a UTF-8 String.
|
|
||||||
* <p>If content is provided and {@link #contentType(MediaType)} is set to
|
|
||||||
* {@code application/x-www-form-urlencoded}, the content will be parsed
|
|
||||||
* and used to populate the {@link #param(String, String...) request
|
|
||||||
* parameters} map.
|
|
||||||
* @param content the body content
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder content(String content) {
|
|
||||||
this.content = content.getBytes(StandardCharsets.UTF_8);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'Content-Type' header of the request.
|
|
||||||
* <p>If content is provided and {@code contentType} is set to
|
|
||||||
* {@code application/x-www-form-urlencoded}, the content will be parsed
|
|
||||||
* and used to populate the {@link #param(String, String...) request
|
|
||||||
* parameters} map.
|
|
||||||
* @param contentType the content type
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder contentType(MediaType contentType) {
|
|
||||||
Assert.notNull(contentType, "'contentType' must not be null");
|
|
||||||
this.contentType = contentType.toString();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'Content-Type' header of the request as a raw String value,
|
|
||||||
* possibly not even well-formed (for testing purposes).
|
|
||||||
* @param contentType the content type
|
|
||||||
* @since 4.1.2
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder contentType(String contentType) {
|
|
||||||
Assert.notNull(contentType, "'contentType' must not be null");
|
|
||||||
this.contentType = contentType;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'Accept' header to the given media type(s).
|
|
||||||
* @param mediaTypes one or more media types
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder accept(MediaType... mediaTypes) {
|
|
||||||
Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty");
|
|
||||||
this.headers.set("Accept", MediaType.toString(Arrays.asList(mediaTypes)));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the {@code Accept} header using raw String values, possibly not even
|
|
||||||
* well-formed (for testing purposes).
|
|
||||||
* @param mediaTypes one or more media types; internally joined as
|
|
||||||
* comma-separated String
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder accept(String... mediaTypes) {
|
|
||||||
Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty");
|
|
||||||
this.headers.set("Accept", String.join(", ", mediaTypes));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a header to the request. Values are always added.
|
|
||||||
* @param name the header name
|
|
||||||
* @param values one or more header values
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder header(String name, Object... values) {
|
|
||||||
addToMultiValueMap(this.headers, name, values);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add all headers to the request. Values are always added.
|
|
||||||
* @param httpHeaders the headers and values to add
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders) {
|
|
||||||
httpHeaders.forEach(this.headers::addAll);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a request parameter to {@link MockHttpServletRequest#getParameterMap()}.
|
|
||||||
* <p>In the Servlet API, a request parameter may be parsed from the query
|
|
||||||
* string and/or from the body of an {@code application/x-www-form-urlencoded}
|
|
||||||
* request. This method simply adds to the request parameter map. You may
|
|
||||||
* also use add Servlet request parameters by specifying the query or form
|
|
||||||
* data through one of the following:
|
|
||||||
* <ul>
|
|
||||||
* <li>Supply a URI with a query to {@link MockMvcRequestBuilders}.
|
|
||||||
* <li>Add query params via {@link #queryParam} or {@link #queryParams}.
|
|
||||||
* <li>Provide {@link #content} with {@link #contentType}
|
|
||||||
* {@code application/x-www-form-urlencoded}.
|
|
||||||
* </ul>
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param values one or more values
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder param(String name, String... values) {
|
|
||||||
addToMultiValueMap(this.parameters, name, values);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Variant of {@link #param(String, String...)} with a {@link MultiValueMap}.
|
|
||||||
* @param params the parameters to add
|
|
||||||
* @since 4.2.4
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder params(MultiValueMap<String, String> params) {
|
|
||||||
params.forEach((name, values) -> {
|
|
||||||
for (String value : values) {
|
|
||||||
this.parameters.add(name, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append to the query string and also add to the
|
|
||||||
* {@link #param(String, String...) request parameters} map. The parameter
|
|
||||||
* name and value are encoded when they are added to the query string.
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param values one or more values
|
|
||||||
* @since 5.2.2
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder queryParam(String name, String... values) {
|
|
||||||
param(name, values);
|
|
||||||
this.queryParams.addAll(name, Arrays.asList(values));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append to the query string and also add to the
|
|
||||||
* {@link #params(MultiValueMap) request parameters} map. The parameter
|
|
||||||
* name and value are encoded when they are added to the query string.
|
|
||||||
* @param params the parameters to add
|
|
||||||
* @since 5.2.2
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder queryParams(MultiValueMap<String, String> params) {
|
|
||||||
params(params);
|
|
||||||
this.queryParams.addAll(params);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append the given value(s) to the given form field and also add them to the
|
|
||||||
* {@linkplain #param(String, String...) request parameters} map.
|
|
||||||
* @param name the field name
|
|
||||||
* @param values one or more values
|
|
||||||
* @since 6.1.7
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder formField(String name, String... values) {
|
|
||||||
param(name, values);
|
|
||||||
this.formFields.addAll(name, Arrays.asList(values));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Variant of {@link #formField(String, String...)} with a {@link MultiValueMap}.
|
|
||||||
* @param formFields the form fields to add
|
|
||||||
* @since 6.1.7
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder formFields(MultiValueMap<String, String> formFields) {
|
|
||||||
params(formFields);
|
|
||||||
this.formFields.addAll(formFields);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the given cookies to the request. Cookies are always added.
|
|
||||||
* @param cookies the cookies to add
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder cookie(Cookie... cookies) {
|
|
||||||
Assert.notEmpty(cookies, "'cookies' must not be empty");
|
|
||||||
this.cookies.addAll(Arrays.asList(cookies));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the specified locales as preferred request locales.
|
|
||||||
* @param locales the locales to add
|
|
||||||
* @since 4.3.6
|
|
||||||
* @see #locale(Locale)
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder locale(Locale... locales) {
|
|
||||||
Assert.notEmpty(locales, "'locales' must not be empty");
|
|
||||||
this.locales.addAll(Arrays.asList(locales));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the locale of the request, overriding any previous locales.
|
|
||||||
* @param locale the locale, or {@code null} to reset it
|
|
||||||
* @see #locale(Locale...)
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder locale(@Nullable Locale locale) {
|
|
||||||
this.locales.clear();
|
|
||||||
if (locale != null) {
|
|
||||||
this.locales.add(locale);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a request attribute.
|
|
||||||
* @param name the attribute name
|
|
||||||
* @param value the attribute value
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder requestAttr(String name, Object value) {
|
|
||||||
addToMap(this.requestAttributes, name, value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a session attribute.
|
|
||||||
* @param name the session attribute name
|
|
||||||
* @param value the session attribute value
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder sessionAttr(String name, Object value) {
|
|
||||||
addToMap(this.sessionAttributes, name, value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set session attributes.
|
|
||||||
* @param sessionAttributes the session attributes
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder sessionAttrs(Map<String, Object> sessionAttributes) {
|
|
||||||
Assert.notEmpty(sessionAttributes, "'sessionAttributes' must not be empty");
|
|
||||||
sessionAttributes.forEach(this::sessionAttr);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an "input" flash attribute.
|
|
||||||
* @param name the flash attribute name
|
|
||||||
* @param value the flash attribute value
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder flashAttr(String name, Object value) {
|
|
||||||
addToMap(this.flashAttributes, name, value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set flash attributes.
|
|
||||||
* @param flashAttributes the flash attributes
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder flashAttrs(Map<String, Object> flashAttributes) {
|
|
||||||
Assert.notEmpty(flashAttributes, "'flashAttributes' must not be empty");
|
|
||||||
flashAttributes.forEach(this::flashAttr);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the HTTP session to use, possibly re-used across requests.
|
|
||||||
* <p>Individual attributes provided via {@link #sessionAttr(String, Object)}
|
|
||||||
* override the content of the session provided here.
|
|
||||||
* @param session the HTTP session
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder session(MockHttpSession session) {
|
|
||||||
Assert.notNull(session, "'session' must not be null");
|
|
||||||
this.session = session;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the principal of the request.
|
|
||||||
* @param principal the principal
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder principal(Principal principal) {
|
|
||||||
Assert.notNull(principal, "'principal' must not be null");
|
|
||||||
this.principal = principal;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the remote address of the request.
|
|
||||||
* @param remoteAddress the remote address (IP)
|
|
||||||
* @since 6.0.10
|
|
||||||
*/
|
|
||||||
public MockHttpServletRequestBuilder remoteAddress(String remoteAddress) {
|
|
||||||
Assert.hasText(remoteAddress, "'remoteAddress' must not be null or blank");
|
|
||||||
this.remoteAddress = remoteAddress;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An extension point for further initialization of {@link MockHttpServletRequest}
|
|
||||||
* in ways not built directly into the {@code MockHttpServletRequestBuilder}.
|
|
||||||
* Implementation of this interface can have builder-style methods themselves
|
|
||||||
* and be made accessible through static factory methods.
|
|
||||||
* @param postProcessor a post-processor to add
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor) {
|
|
||||||
Assert.notNull(postProcessor, "postProcessor is required");
|
|
||||||
this.postProcessors.add(postProcessor);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
* @return always returns {@code true}.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean isMergeEnabled() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merges the properties of the "parent" RequestBuilder accepting values
|
|
||||||
* only if not already set in "this" instance.
|
|
||||||
* @param parent the parent {@code RequestBuilder} to inherit properties from
|
|
||||||
* @return the result of the merge
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object merge(@Nullable Object parent) {
|
|
||||||
if (parent == null) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
if (!(parent instanceof MockHttpServletRequestBuilder parentBuilder)) {
|
|
||||||
throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]");
|
|
||||||
}
|
|
||||||
if (!StringUtils.hasText(this.contextPath)) {
|
|
||||||
this.contextPath = parentBuilder.contextPath;
|
|
||||||
}
|
|
||||||
if (!StringUtils.hasText(this.servletPath)) {
|
|
||||||
this.servletPath = parentBuilder.servletPath;
|
|
||||||
}
|
|
||||||
if ("".equals(this.pathInfo)) {
|
|
||||||
this.pathInfo = parentBuilder.pathInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.secure == null) {
|
|
||||||
this.secure = parentBuilder.secure;
|
|
||||||
}
|
|
||||||
if (this.principal == null) {
|
|
||||||
this.principal = parentBuilder.principal;
|
|
||||||
}
|
|
||||||
if (this.session == null) {
|
|
||||||
this.session = parentBuilder.session;
|
|
||||||
}
|
|
||||||
if (this.remoteAddress == null) {
|
|
||||||
this.remoteAddress = parentBuilder.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.characterEncoding == null) {
|
|
||||||
this.characterEncoding = parentBuilder.characterEncoding;
|
|
||||||
}
|
|
||||||
if (this.content == null) {
|
|
||||||
this.content = parentBuilder.content;
|
|
||||||
}
|
|
||||||
if (this.contentType == null) {
|
|
||||||
this.contentType = parentBuilder.contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<String, List<Object>> entry : parentBuilder.headers.entrySet()) {
|
|
||||||
String headerName = entry.getKey();
|
|
||||||
if (!this.headers.containsKey(headerName)) {
|
|
||||||
this.headers.put(headerName, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, List<String>> entry : parentBuilder.parameters.entrySet()) {
|
|
||||||
String paramName = entry.getKey();
|
|
||||||
if (!this.parameters.containsKey(paramName)) {
|
|
||||||
this.parameters.put(paramName, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, List<String>> entry : parentBuilder.queryParams.entrySet()) {
|
|
||||||
String paramName = entry.getKey();
|
|
||||||
if (!this.queryParams.containsKey(paramName)) {
|
|
||||||
this.queryParams.put(paramName, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, List<String>> entry : parentBuilder.formFields.entrySet()) {
|
|
||||||
String paramName = entry.getKey();
|
|
||||||
if (!this.formFields.containsKey(paramName)) {
|
|
||||||
this.formFields.put(paramName, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Cookie cookie : parentBuilder.cookies) {
|
|
||||||
if (!containsCookie(cookie)) {
|
|
||||||
this.cookies.add(cookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Locale locale : parentBuilder.locales) {
|
|
||||||
if (!this.locales.contains(locale)) {
|
|
||||||
this.locales.add(locale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<String, Object> entry : parentBuilder.requestAttributes.entrySet()) {
|
|
||||||
String attributeName = entry.getKey();
|
|
||||||
if (!this.requestAttributes.containsKey(attributeName)) {
|
|
||||||
this.requestAttributes.put(attributeName, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, Object> entry : parentBuilder.sessionAttributes.entrySet()) {
|
|
||||||
String attributeName = entry.getKey();
|
|
||||||
if (!this.sessionAttributes.containsKey(attributeName)) {
|
|
||||||
this.sessionAttributes.put(attributeName, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, Object> entry : parentBuilder.flashAttributes.entrySet()) {
|
|
||||||
String attributeName = entry.getKey();
|
|
||||||
if (!this.flashAttributes.containsKey(attributeName)) {
|
|
||||||
this.flashAttributes.put(attributeName, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.postProcessors.addAll(0, parentBuilder.postProcessors);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean containsCookie(Cookie cookie) {
|
|
||||||
for (Cookie cookieToCheck : this.cookies) {
|
|
||||||
if (ObjectUtils.nullSafeEquals(cookieToCheck.getName(), cookie.getName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a {@link MockHttpServletRequest}.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public final MockHttpServletRequest buildRequest(ServletContext servletContext) {
|
|
||||||
MockHttpServletRequest request = createServletRequest(servletContext);
|
|
||||||
|
|
||||||
request.setAsyncSupported(true);
|
|
||||||
request.setMethod(this.method.name());
|
|
||||||
|
|
||||||
String requestUri = this.uri.getRawPath();
|
|
||||||
request.setRequestURI(requestUri);
|
|
||||||
|
|
||||||
if (this.uri.getScheme() != null) {
|
|
||||||
request.setScheme(this.uri.getScheme());
|
|
||||||
}
|
|
||||||
if (this.uri.getHost() != null) {
|
|
||||||
request.setServerName(this.uri.getHost());
|
|
||||||
}
|
|
||||||
if (this.uri.getPort() != -1) {
|
|
||||||
request.setServerPort(this.uri.getPort());
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePathRequestProperties(request, requestUri);
|
|
||||||
|
|
||||||
if (this.secure != null) {
|
|
||||||
request.setSecure(this.secure);
|
|
||||||
}
|
|
||||||
if (this.principal != null) {
|
|
||||||
request.setUserPrincipal(this.principal);
|
|
||||||
}
|
|
||||||
if (this.remoteAddress != null) {
|
|
||||||
request.setRemoteAddr(this.remoteAddress);
|
|
||||||
}
|
|
||||||
if (this.session != null) {
|
|
||||||
request.setSession(this.session);
|
|
||||||
}
|
|
||||||
|
|
||||||
request.setCharacterEncoding(this.characterEncoding);
|
|
||||||
request.setContent(this.content);
|
|
||||||
request.setContentType(this.contentType);
|
|
||||||
|
|
||||||
this.headers.forEach((name, values) -> {
|
|
||||||
for (Object value : values) {
|
|
||||||
request.addHeader(name, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!ObjectUtils.isEmpty(this.content) &&
|
|
||||||
!this.headers.containsKey(HttpHeaders.CONTENT_LENGTH) &&
|
|
||||||
!this.headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
|
|
||||||
|
|
||||||
request.addHeader(HttpHeaders.CONTENT_LENGTH, this.content.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
String query = this.uri.getRawQuery();
|
|
||||||
if (!this.queryParams.isEmpty()) {
|
|
||||||
String str = UriComponentsBuilder.newInstance().queryParams(this.queryParams).build().encode().getQuery();
|
|
||||||
query = StringUtils.hasLength(query) ? (query + "&" + str) : str;
|
|
||||||
}
|
|
||||||
if (query != null) {
|
|
||||||
request.setQueryString(query);
|
|
||||||
}
|
|
||||||
addRequestParams(request, UriComponentsBuilder.fromUri(this.uri).build().getQueryParams());
|
|
||||||
|
|
||||||
this.parameters.forEach((name, values) -> {
|
|
||||||
for (String value : values) {
|
|
||||||
request.addParameter(name, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.formFields.isEmpty()) {
|
|
||||||
if (this.content != null && this.content.length > 0) {
|
|
||||||
throw new IllegalStateException("Could not write form data with an existing body");
|
|
||||||
}
|
|
||||||
Charset charset = (this.characterEncoding != null ?
|
|
||||||
Charset.forName(this.characterEncoding) : StandardCharsets.UTF_8);
|
|
||||||
MediaType mediaType = (request.getContentType() != null ?
|
|
||||||
MediaType.parseMediaType(request.getContentType()) :
|
|
||||||
new MediaType(MediaType.APPLICATION_FORM_URLENCODED, charset));
|
|
||||||
if (!mediaType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)) {
|
|
||||||
throw new IllegalStateException("Invalid content type: '" + mediaType +
|
|
||||||
"' is not compatible with '" + MediaType.APPLICATION_FORM_URLENCODED + "'");
|
|
||||||
}
|
|
||||||
request.setContent(writeFormData(mediaType, charset));
|
|
||||||
if (request.getContentType() == null) {
|
|
||||||
request.setContentType(mediaType.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.content != null && this.content.length > 0) {
|
|
||||||
String requestContentType = request.getContentType();
|
|
||||||
if (requestContentType != null) {
|
|
||||||
try {
|
|
||||||
MediaType mediaType = MediaType.parseMediaType(requestContentType);
|
|
||||||
if (MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)) {
|
|
||||||
addRequestParams(request, parseFormData(mediaType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
// Must be invalid, ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ObjectUtils.isEmpty(this.cookies)) {
|
|
||||||
request.setCookies(this.cookies.toArray(new Cookie[0]));
|
|
||||||
}
|
|
||||||
if (!ObjectUtils.isEmpty(this.locales)) {
|
|
||||||
request.setPreferredLocales(this.locales);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.requestAttributes.forEach(request::setAttribute);
|
|
||||||
this.sessionAttributes.forEach((name, attribute) -> {
|
|
||||||
HttpSession session = request.getSession();
|
|
||||||
Assert.state(session != null, "No HttpSession");
|
|
||||||
session.setAttribute(name, attribute);
|
|
||||||
});
|
|
||||||
|
|
||||||
FlashMap flashMap = new FlashMap();
|
|
||||||
flashMap.putAll(this.flashAttributes);
|
|
||||||
FlashMapManager flashMapManager = getFlashMapManager(request);
|
|
||||||
flashMapManager.saveOutputFlashMap(flashMap, request, new MockHttpServletResponse());
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link MockHttpServletRequest} based on the supplied
|
|
||||||
* {@code ServletContext}.
|
|
||||||
* <p>Can be overridden in subclasses.
|
|
||||||
*/
|
|
||||||
protected MockHttpServletRequest createServletRequest(ServletContext servletContext) {
|
|
||||||
return new MockHttpServletRequest(servletContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the contextPath, servletPath, and pathInfo of the request.
|
|
||||||
*/
|
|
||||||
private void updatePathRequestProperties(MockHttpServletRequest request, String requestUri) {
|
|
||||||
if (!requestUri.startsWith(this.contextPath)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Request URI [" + requestUri + "] does not start with context path [" + this.contextPath + "]");
|
|
||||||
}
|
|
||||||
request.setContextPath(this.contextPath);
|
|
||||||
request.setServletPath(this.servletPath);
|
|
||||||
|
|
||||||
if ("".equals(this.pathInfo)) {
|
|
||||||
if (!requestUri.startsWith(this.contextPath + this.servletPath)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Invalid servlet path [" + this.servletPath + "] for request URI [" + requestUri + "]");
|
|
||||||
}
|
|
||||||
String extraPath = requestUri.substring(this.contextPath.length() + this.servletPath.length());
|
|
||||||
this.pathInfo = (StringUtils.hasText(extraPath) ?
|
|
||||||
UrlPathHelper.defaultInstance.decodeRequestString(request, extraPath) : null);
|
|
||||||
}
|
|
||||||
request.setPathInfo(this.pathInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addRequestParams(MockHttpServletRequest request, MultiValueMap<String, String> map) {
|
|
||||||
map.forEach((key, values) -> values.forEach(value -> {
|
|
||||||
value = (value != null ? UriUtils.decode(value, StandardCharsets.UTF_8) : null);
|
|
||||||
request.addParameter(UriUtils.decode(key, StandardCharsets.UTF_8), value);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] writeFormData(MediaType mediaType, Charset charset) {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
HttpOutputMessage message = new HttpOutputMessage() {
|
|
||||||
@Override
|
|
||||||
public OutputStream getBody() {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpHeaders getHeaders() {
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(mediaType);
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
FormHttpMessageConverter messageConverter = new FormHttpMessageConverter();
|
|
||||||
messageConverter.setCharset(charset);
|
|
||||||
messageConverter.write(this.formFields, mediaType, message);
|
|
||||||
return out.toByteArray();
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new IllegalStateException("Failed to write form data to request body", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private MultiValueMap<String, String> parseFormData(MediaType mediaType) {
|
|
||||||
HttpInputMessage message = new HttpInputMessage() {
|
|
||||||
@Override
|
|
||||||
public InputStream getBody() {
|
|
||||||
byte[] bodyContent = MockHttpServletRequestBuilder.this.content;
|
|
||||||
return (bodyContent != null ? new ByteArrayInputStream(bodyContent) : InputStream.nullInputStream());
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public HttpHeaders getHeaders() {
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.setContentType(mediaType);
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
return (MultiValueMap<String, String>) new FormHttpMessageConverter().read(null, message);
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new IllegalStateException("Failed to parse form data in request body", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private FlashMapManager getFlashMapManager(MockHttpServletRequest request) {
|
|
||||||
FlashMapManager flashMapManager = null;
|
|
||||||
try {
|
|
||||||
ServletContext servletContext = request.getServletContext();
|
|
||||||
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
|
|
||||||
flashMapManager = wac.getBean(DispatcherServlet.FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
|
|
||||||
}
|
|
||||||
catch (IllegalStateException | NoSuchBeanDefinitionException ex) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return (flashMapManager != null ? flashMapManager : new SessionFlashMapManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
|
|
||||||
for (RequestPostProcessor postProcessor : this.postProcessors) {
|
|
||||||
request = postProcessor.postProcessRequest(request);
|
|
||||||
}
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static void addToMap(Map<String, Object> map, String name, Object value) {
|
|
||||||
Assert.hasLength(name, "'name' must not be empty");
|
|
||||||
Assert.notNull(value, "'value' must not be null");
|
|
||||||
map.put(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> void addToMultiValueMap(MultiValueMap<String, T> map, String name, T[] values) {
|
|
||||||
Assert.hasLength(name, "'name' must not be empty");
|
|
||||||
Assert.notEmpty(values, "'values' must not be empty");
|
|
||||||
for (T value : values) {
|
|
||||||
map.add(name, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ import org.springframework.util.MultiValueMap;
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*/
|
*/
|
||||||
public class MockMultipartHttpServletRequestBuilder extends MockHttpServletRequestBuilder {
|
public class MockMultipartHttpServletRequestBuilder extends AbstractMockHttpServletRequestBuilder<MockMultipartHttpServletRequestBuilder> {
|
||||||
|
|
||||||
private final List<MockMultipartFile> files = new ArrayList<>();
|
private final List<MockMultipartFile> files = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -73,7 +73,8 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque
|
||||||
* @since 5.3.22
|
* @since 5.3.22
|
||||||
*/
|
*/
|
||||||
MockMultipartHttpServletRequestBuilder(HttpMethod httpMethod, String uriTemplate, Object... uriVariables) {
|
MockMultipartHttpServletRequestBuilder(HttpMethod httpMethod, String uriTemplate, Object... uriVariables) {
|
||||||
super(httpMethod, uriTemplate, uriVariables);
|
super(httpMethod);
|
||||||
|
super.uri(uriTemplate, uriVariables);
|
||||||
super.contentType(MediaType.MULTIPART_FORM_DATA);
|
super.contentType(MediaType.MULTIPART_FORM_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +93,8 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque
|
||||||
* @since 5.3.21
|
* @since 5.3.21
|
||||||
*/
|
*/
|
||||||
MockMultipartHttpServletRequestBuilder(HttpMethod httpMethod, URI uri) {
|
MockMultipartHttpServletRequestBuilder(HttpMethod httpMethod, URI uri) {
|
||||||
super(httpMethod, uri);
|
super(httpMethod);
|
||||||
|
super.uri(uri);
|
||||||
super.contentType(MediaType.MULTIPART_FORM_DATA);
|
super.contentType(MediaType.MULTIPART_FORM_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +136,7 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
if (parent instanceof MockHttpServletRequestBuilder) {
|
if (parent instanceof AbstractMockHttpServletRequestBuilder) {
|
||||||
super.merge(parent);
|
super.merge(parent);
|
||||||
if (parent instanceof MockMultipartHttpServletRequestBuilder parentBuilder) {
|
if (parent instanceof MockMultipartHttpServletRequestBuilder parentBuilder) {
|
||||||
this.files.addAll(parentBuilder.files);
|
this.files.addAll(parentBuilder.files);
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.springframework.util.MultiValueMap
|
||||||
import java.security.Principal
|
import java.security.Principal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import jakarta.servlet.http.Cookie
|
import jakarta.servlet.http.Cookie
|
||||||
|
import org.springframework.test.web.servlet.request.AbstractMockHttpServletRequestBuilder
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a [MockHttpServletRequestBuilder] Kotlin DSL in order to be able to write idiomatic Kotlin code.
|
* Provide a [MockHttpServletRequestBuilder] Kotlin DSL in order to be able to write idiomatic Kotlin code.
|
||||||
|
@ -40,7 +41,7 @@ import jakarta.servlet.http.Cookie
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
open class MockHttpServletRequestDsl internal constructor (private val builder: MockHttpServletRequestBuilder) {
|
open class MockHttpServletRequestDsl internal constructor (private val builder: AbstractMockHttpServletRequestBuilder<*>) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see [MockHttpServletRequestBuilder.contextPath]
|
* @see [MockHttpServletRequestBuilder.contextPath]
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2024 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
|
||||||
|
*
|
||||||
|
* https://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.assertj;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||||
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link MockMvcTester} that use the methods that
|
||||||
|
* integrate with {@link MockMvc} way of building the requests and
|
||||||
|
* asserting the responses.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@SpringJUnitConfig
|
||||||
|
@WebAppConfiguration
|
||||||
|
class MockMvcTesterCompatibilityIntegrationTests {
|
||||||
|
|
||||||
|
private final MockMvcTester mvc;
|
||||||
|
|
||||||
|
MockMvcTesterCompatibilityIntegrationTests(@Autowired WebApplicationContext wac) {
|
||||||
|
this.mvc = MockMvcTester.from(wac);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void performGet() {
|
||||||
|
assertThat(this.mvc.perform(get("/greet"))).hasStatusOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void performGetWithInvalidMediaTypeAssertion() {
|
||||||
|
MvcTestResult result = this.mvc.perform(get("/greet"));
|
||||||
|
assertThatExceptionOfType(AssertionError.class)
|
||||||
|
.isThrownBy(() -> assertThat(result).hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON))
|
||||||
|
.withMessageContaining("is compatible with 'application/json'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void assertHttpStatusCode() {
|
||||||
|
assertThat(this.mvc.get().uri("/greet")).matches(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
@Import(TestController.class)
|
||||||
|
static class WebConfiguration {
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class TestController {
|
||||||
|
|
||||||
|
@GetMapping(path = "/greet", produces = "text/plain")
|
||||||
|
String greet() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(path = "/message", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
String message() {
|
||||||
|
return "{\"message\": \"hello\"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||||
import org.springframework.test.context.web.WebAppConfiguration;
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
import org.springframework.test.web.Person;
|
import org.springframework.test.web.Person;
|
||||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
import org.springframework.test.web.servlet.ResultMatcher;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.validation.Errors;
|
import org.springframework.validation.Errors;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
@ -63,9 +63,8 @@ import static java.util.Map.entry;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.assertj.core.api.InstanceOfAssertFactories.map;
|
import static org.assertj.core.api.InstanceOfAssertFactories.map;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for {@link MockMvcTester}.
|
* Integration tests for {@link MockMvcTester}.
|
||||||
|
@ -77,10 +76,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
@WebAppConfiguration
|
@WebAppConfiguration
|
||||||
public class MockMvcTesterIntegrationTests {
|
public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
private final MockMvcTester mockMvc;
|
private final MockMvcTester mvc;
|
||||||
|
|
||||||
MockMvcTesterIntegrationTests(WebApplicationContext wac) {
|
MockMvcTesterIntegrationTests(WebApplicationContext wac) {
|
||||||
this.mockMvc = MockMvcTester.from(wac);
|
this.mvc = MockMvcTester.from(wac);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
@ -88,24 +87,24 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasAsyncStartedTrue() {
|
void hasAsyncStartedTrue() {
|
||||||
assertThat(perform(get("/callable").accept(MediaType.APPLICATION_JSON)))
|
assertThat(mvc.get().uri("/callable").accept(MediaType.APPLICATION_JSON))
|
||||||
.request().hasAsyncStarted(true);
|
.request().hasAsyncStarted(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasAsyncStartedFalse() {
|
void hasAsyncStartedFalse() {
|
||||||
assertThat(perform(get("/greet"))).request().hasAsyncStarted(false);
|
assertThat(mvc.get().uri("/greet")).request().hasAsyncStarted(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void attributes() {
|
void attributes() {
|
||||||
assertThat(perform(get("/greet"))).request().attributes()
|
assertThat(mvc.get().uri("/greet")).request().attributes()
|
||||||
.containsKey(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
|
.containsKey(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void sessionAttributes() {
|
void sessionAttributes() {
|
||||||
assertThat(perform(get("/locale"))).request().sessionAttributes()
|
assertThat(mvc.get().uri("/locale")).request().sessionAttributes()
|
||||||
.containsOnly(entry("locale", Locale.UK));
|
.containsOnly(entry("locale", Locale.UK));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,17 +115,17 @@ public class MockMvcTesterIntegrationTests {
|
||||||
@Test
|
@Test
|
||||||
void containsCookie() {
|
void containsCookie() {
|
||||||
Cookie cookie = new Cookie("test", "value");
|
Cookie cookie = new Cookie("test", "value");
|
||||||
assertThat(performWithCookie(cookie, get("/greet"))).cookies().containsCookie("test");
|
assertThat(withCookie(cookie).get().uri("/greet")).cookies().containsCookie("test");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasValue() {
|
void hasValue() {
|
||||||
Cookie cookie = new Cookie("test", "value");
|
Cookie cookie = new Cookie("test", "value");
|
||||||
assertThat(performWithCookie(cookie, get("/greet"))).cookies().hasValue("test", "value");
|
assertThat(withCookie(cookie).get().uri("/greet")).cookies().hasValue("test", "value");
|
||||||
}
|
}
|
||||||
|
|
||||||
private MvcTestResult performWithCookie(Cookie cookie, MockHttpServletRequestBuilder request) {
|
private MockMvcTester withCookie(Cookie cookie) {
|
||||||
MockMvcTester mockMvc = MockMvcTester.of(List.of(new TestController()), builder -> builder.addInterceptors(
|
return MockMvcTester.of(List.of(new TestController()), builder -> builder.addInterceptors(
|
||||||
new HandlerInterceptor() {
|
new HandlerInterceptor() {
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||||
|
@ -134,7 +133,6 @@ public class MockMvcTesterIntegrationTests {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}).build());
|
}).build());
|
||||||
return mockMvc.perform(request);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,12 +141,12 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void statusOk() {
|
void statusOk() {
|
||||||
assertThat(perform(get("/greet"))).hasStatusOk();
|
assertThat(mvc.get().uri("/greet")).hasStatusOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void statusSeries() {
|
void statusSeries() {
|
||||||
assertThat(perform(get("/greet"))).hasStatus2xxSuccessful();
|
assertThat(mvc.get().uri("/greet")).hasStatus2xxSuccessful();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -158,13 +156,13 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldAssertHeader() {
|
void shouldAssertHeader() {
|
||||||
assertThat(perform(get("/greet")))
|
assertThat(mvc.get().uri("/greet"))
|
||||||
.hasHeader("Content-Type", "text/plain;charset=ISO-8859-1");
|
.hasHeader("Content-Type", "text/plain;charset=ISO-8859-1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldAssertHeaderWithCallback() {
|
void shouldAssertHeaderWithCallback() {
|
||||||
assertThat(perform(get("/greet"))).headers().satisfies(textContent("ISO-8859-1"));
|
assertThat(mvc.get().uri("/greet")).headers().satisfies(textContent("ISO-8859-1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Consumer<HttpHeaders> textContent(String charset) {
|
private Consumer<HttpHeaders> textContent(String charset) {
|
||||||
|
@ -179,33 +177,33 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasViewName() {
|
void hasViewName() {
|
||||||
assertThat(perform(get("/persons/{0}", "Andy"))).hasViewName("persons/index");
|
assertThat(mvc.get().uri("/persons/{0}", "Andy")).hasViewName("persons/index");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void viewNameWithCustomAssertion() {
|
void viewNameWithCustomAssertion() {
|
||||||
assertThat(perform(get("/persons/{0}", "Andy"))).viewName().startsWith("persons");
|
assertThat(mvc.get().uri("/persons/{0}", "Andy")).viewName().startsWith("persons");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void containsAttributes() {
|
void containsAttributes() {
|
||||||
assertThat(perform(post("/persons").param("name", "Andy"))).model()
|
assertThat(mvc.post().uri("/persons").param("name", "Andy")).model()
|
||||||
.containsOnlyKeys("name").containsEntry("name", "Andy");
|
.containsOnlyKeys("name").containsEntry("name", "Andy");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasErrors() {
|
void hasErrors() {
|
||||||
assertThat(perform(post("/persons"))).model().hasErrors();
|
assertThat(mvc.post().uri("/persons")).model().hasErrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasAttributeErrors() {
|
void hasAttributeErrors() {
|
||||||
assertThat(perform(post("/persons"))).model().hasAttributeErrors("person");
|
assertThat(mvc.post().uri("/persons")).model().hasAttributeErrors("person");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasAttributeErrorsCount() {
|
void hasAttributeErrorsCount() {
|
||||||
assertThat(perform(post("/persons"))).model().extractingBindingResult("person").hasErrorsCount(1);
|
assertThat(mvc.post().uri("/persons")).model().extractingBindingResult("person").hasErrorsCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -215,7 +213,7 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void containsAttributes() {
|
void containsAttributes() {
|
||||||
assertThat(perform(post("/persons").param("name", "Andy"))).flash()
|
assertThat(mvc.post().uri("/persons").param("name", "Andy")).flash()
|
||||||
.containsOnlyKeys("message").hasEntrySatisfying("message",
|
.containsOnlyKeys("message").hasEntrySatisfying("message",
|
||||||
value -> assertThat(value).isInstanceOfSatisfying(String.class,
|
value -> assertThat(value).isInstanceOfSatisfying(String.class,
|
||||||
stringValue -> assertThat(stringValue).startsWith("success")));
|
stringValue -> assertThat(stringValue).startsWith("success")));
|
||||||
|
@ -227,31 +225,31 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void asyncResult() {
|
void asyncResult() {
|
||||||
assertThat(perform(get("/callable").accept(MediaType.APPLICATION_JSON)))
|
assertThat(mvc.get().uri("/callable").accept(MediaType.APPLICATION_JSON))
|
||||||
.asyncResult().asInstanceOf(map(String.class, Object.class))
|
.asyncResult().asInstanceOf(map(String.class, Object.class))
|
||||||
.containsOnly(entry("key", "value"));
|
.containsOnly(entry("key", "value"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void stringContent() {
|
void stringContent() {
|
||||||
assertThat(perform(get("/greet"))).body().asString().isEqualTo("hello");
|
assertThat(mvc.get().uri("/greet")).body().asString().isEqualTo("hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void jsonPathContent() {
|
void jsonPathContent() {
|
||||||
assertThat(perform(get("/message"))).bodyJson()
|
assertThat(mvc.get().uri("/message")).bodyJson()
|
||||||
.extractingPath("$.message").asString().isEqualTo("hello");
|
.extractingPath("$.message").asString().isEqualTo("hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void jsonContentCanLoadResourceFromClasspath() {
|
void jsonContentCanLoadResourceFromClasspath() {
|
||||||
assertThat(perform(get("/message"))).bodyJson().isLenientlyEqualTo(
|
assertThat(mvc.get().uri("/message")).bodyJson().isLenientlyEqualTo(
|
||||||
new ClassPathResource("message.json", MockMvcTesterIntegrationTests.class));
|
new ClassPathResource("message.json", MockMvcTesterIntegrationTests.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void jsonContentUsingResourceLoaderClass() {
|
void jsonContentUsingResourceLoaderClass() {
|
||||||
assertThat(perform(get("/message"))).bodyJson().withResourceLoadClass(MockMvcTesterIntegrationTests.class)
|
assertThat(mvc.get().uri("/message")).bodyJson().withResourceLoadClass(MockMvcTesterIntegrationTests.class)
|
||||||
.isLenientlyEqualTo("message.json");
|
.isLenientlyEqualTo("message.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,22 +260,22 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void handlerOn404() {
|
void handlerOn404() {
|
||||||
assertThat(perform(get("/unknown-resource"))).handler().isNull();
|
assertThat(mvc.get().uri("/unknown-resource")).handler().isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasType() {
|
void hasType() {
|
||||||
assertThat(perform(get("/greet"))).handler().hasType(TestController.class);
|
assertThat(mvc.get().uri("/greet")).handler().hasType(TestController.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void isMethodHandler() {
|
void isMethodHandler() {
|
||||||
assertThat(perform(get("/greet"))).handler().isMethodHandler();
|
assertThat(mvc.get().uri("/greet")).handler().isMethodHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void isInvokedOn() {
|
void isInvokedOn() {
|
||||||
assertThat(perform(get("/callable"))).handler()
|
assertThat(mvc.get().uri("/callable")).handler()
|
||||||
.isInvokedOn(AsyncController.class, AsyncController::getCallable);
|
.isInvokedOn(AsyncController.class, AsyncController::getCallable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,31 +286,31 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void doesNotHaveUnresolvedException() {
|
void doesNotHaveUnresolvedException() {
|
||||||
assertThat(perform(get("/greet"))).doesNotHaveUnresolvedException();
|
assertThat(mvc.get().uri("/greet")).doesNotHaveUnresolvedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasUnresolvedException() {
|
void hasUnresolvedException() {
|
||||||
assertThat(perform(get("/error/1"))).hasUnresolvedException();
|
assertThat(mvc.get().uri("/error/1")).hasUnresolvedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void doesNotHaveUnresolvedExceptionWithUnresolvedException() {
|
void doesNotHaveUnresolvedExceptionWithUnresolvedException() {
|
||||||
assertThatExceptionOfType(AssertionError.class)
|
assertThatExceptionOfType(AssertionError.class)
|
||||||
.isThrownBy(() -> assertThat(perform(get("/error/1"))).doesNotHaveUnresolvedException())
|
.isThrownBy(() -> assertThat(mvc.get().uri("/error/1")).doesNotHaveUnresolvedException())
|
||||||
.withMessage("Expected request to succeed, but it failed");
|
.withMessage("Expected request to succeed, but it failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasUnresolvedExceptionWithoutUnresolvedException() {
|
void hasUnresolvedExceptionWithoutUnresolvedException() {
|
||||||
assertThatExceptionOfType(AssertionError.class)
|
assertThatExceptionOfType(AssertionError.class)
|
||||||
.isThrownBy(() -> assertThat(perform(get("/greet"))).hasUnresolvedException())
|
.isThrownBy(() -> assertThat(mvc.get().uri("/greet")).hasUnresolvedException())
|
||||||
.withMessage("Expected request to fail, but it succeeded");
|
.withMessage("Expected request to fail, but it succeeded");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void unresolvedExceptionWithFailedRequest() {
|
void unresolvedExceptionWithFailedRequest() {
|
||||||
assertThat(perform(get("/error/1"))).unresolvedException()
|
assertThat(mvc.get().uri("/error/1")).unresolvedException()
|
||||||
.isInstanceOf(ServletException.class)
|
.isInstanceOf(ServletException.class)
|
||||||
.cause().isInstanceOf(IllegalStateException.class).hasMessage("Expected");
|
.cause().isInstanceOf(IllegalStateException.class).hasMessage("Expected");
|
||||||
}
|
}
|
||||||
|
@ -320,7 +318,7 @@ public class MockMvcTesterIntegrationTests {
|
||||||
@Test
|
@Test
|
||||||
void unresolvedExceptionWithSuccessfulRequest() {
|
void unresolvedExceptionWithSuccessfulRequest() {
|
||||||
assertThatExceptionOfType(AssertionError.class)
|
assertThatExceptionOfType(AssertionError.class)
|
||||||
.isThrownBy(() -> assertThat(perform(get("/greet"))).unresolvedException())
|
.isThrownBy(() -> assertThat(mvc.get().uri("/greet")).unresolvedException())
|
||||||
.withMessage("Expected request to fail, but it succeeded");
|
.withMessage("Expected request to fail, but it succeeded");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +404,7 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
|
|
||||||
private void testAssertionFailureWithUnresolvableException(Consumer<MvcTestResult> assertions) {
|
private void testAssertionFailureWithUnresolvableException(Consumer<MvcTestResult> assertions) {
|
||||||
MvcTestResult result = perform(get("/error/1"));
|
MvcTestResult result = mvc.get().uri("/error/1").exchange();
|
||||||
assertThatExceptionOfType(AssertionError.class)
|
assertThatExceptionOfType(AssertionError.class)
|
||||||
.isThrownBy(() -> assertions.accept(result))
|
.isThrownBy(() -> assertions.accept(result))
|
||||||
.withMessageContainingAll("Request failed unexpectedly:",
|
.withMessageContainingAll("Request failed unexpectedly:",
|
||||||
|
@ -418,49 +416,49 @@ public class MockMvcTesterIntegrationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasForwardUrl() {
|
void hasForwardUrl() {
|
||||||
assertThat(perform(get("/persons/John"))).hasForwardedUrl("persons/index");
|
assertThat(mvc.get().uri("/persons/John")).hasForwardedUrl("persons/index");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasRedirectUrl() {
|
void hasRedirectUrl() {
|
||||||
assertThat(perform(post("/persons").param("name", "Andy"))).hasStatus(HttpStatus.FOUND)
|
assertThat(mvc.post().uri("/persons").param("name", "Andy")).hasStatus(HttpStatus.FOUND)
|
||||||
.hasRedirectedUrl("/persons/Andy");
|
.hasRedirectedUrl("/persons/Andy");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void satisfiesAllowsAdditionalAssertions() {
|
void satisfiesAllowsAdditionalAssertions() {
|
||||||
assertThat(this.mockMvc.perform(get("/greet"))).satisfies(result -> {
|
assertThat(mvc.get().uri("/greet")).satisfies(result -> {
|
||||||
assertThat(result).isInstanceOf(MvcTestResult.class);
|
assertThat(result).isInstanceOf(MvcTestResult.class);
|
||||||
assertThat(result).hasStatusOk();
|
assertThat(result).hasStatusOk();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void resultMatcherCanBeReused() {
|
void resultMatcherCanBeReused() throws Exception {
|
||||||
assertThat(this.mockMvc.perform(get("/greet"))).matches(status().isOk());
|
MvcTestResult result = mvc.get().uri("/greet").exchange();
|
||||||
|
ResultMatcher matcher = mock(ResultMatcher.class);
|
||||||
|
assertThat(result).matches(matcher);
|
||||||
|
verify(matcher).match(result.getMvcResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void resultMatcherFailsWithDedicatedException() {
|
void resultMatcherFailsWithDedicatedException() {
|
||||||
|
ResultMatcher matcher = result -> assertThat(result.getResponse().getStatus())
|
||||||
|
.isEqualTo(HttpStatus.NOT_FOUND.value());
|
||||||
assertThatExceptionOfType(AssertionError.class)
|
assertThatExceptionOfType(AssertionError.class)
|
||||||
.isThrownBy(() -> assertThat(this.mockMvc.perform(get("/greet")))
|
.isThrownBy(() -> assertThat(mvc.get().uri("/greet"))
|
||||||
.matches(status().isNotFound()))
|
.matches(matcher))
|
||||||
.withMessageContaining("Status expected:<404> but was:<200>");
|
.withMessageContaining("expected: 404").withMessageContaining(" but was: 200");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldApplyResultHandler() { // Spring RESTDocs example
|
void shouldApplyResultHandler() { // Spring RESTDocs example
|
||||||
AtomicBoolean applied = new AtomicBoolean();
|
AtomicBoolean applied = new AtomicBoolean();
|
||||||
assertThat(this.mockMvc.perform(get("/greet"))).apply(result -> applied.set(true));
|
assertThat(mvc.get().uri("/greet")).apply(result -> applied.set(true));
|
||||||
assertThat(applied).isTrue();
|
assertThat(applied).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private MvcTestResult perform(MockHttpServletRequestBuilder builder) {
|
|
||||||
return this.mockMvc.perform(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
@Import({ TestController.class, PersonController.class, AsyncController.class,
|
@Import({ TestController.class, PersonController.class, AsyncController.class,
|
||||||
|
|
Loading…
Reference in New Issue