diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java index 9b6cf1c8fde..6c681e54799 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java @@ -119,6 +119,8 @@ public class MockHttpServletRequestBuilder private final MultiValueMap parameters = new LinkedMultiValueMap<>(); + private final MultiValueMap queryParams = new LinkedMultiValueMap<>(); + private final List cookies = new ArrayList<>(); private final List locales = new ArrayList<>(); @@ -248,6 +250,10 @@ public class MockHttpServletRequestBuilder /** * Set the request body. + *

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) { @@ -257,6 +263,10 @@ public class MockHttpServletRequestBuilder /** * Set the request body as a UTF-8 String. + *

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) { @@ -266,6 +276,10 @@ public class MockHttpServletRequestBuilder /** * Set the 'Content-Type' header of the request. + *

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) { @@ -328,8 +342,18 @@ public class MockHttpServletRequestBuilder } /** - * Add a request parameter to the {@link MockHttpServletRequest}. - *

If called more than once, new values get added to existing ones. + * Add a request parameter to {@link MockHttpServletRequest#getParameterMap()}. + *

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: + *

    + *
  • Supply a URL with a query to {@link MockMvcRequestBuilders}. + *
  • Add query params via {@link #queryParam} or {@link #queryParams}. + *
  • Provide {@link #content} with {@link #contentType} + * {@code application/x-www-form-urlencoded}. + *
* @param name the parameter name * @param values one or more values */ @@ -339,9 +363,7 @@ public class MockHttpServletRequestBuilder } /** - * Add a map of request parameters to the {@link MockHttpServletRequest}, - * for example when testing a form submission. - *

If called more than once, new values get added to existing ones. + * Variant of {@link #param(String, String...)} with a {@link MultiValueMap}. * @param params the parameters to add * @since 4.2.4 */ @@ -354,6 +376,33 @@ public class MockHttpServletRequestBuilder 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 params) { + params(params); + this.queryParams.addAll(params); + return this; + } + /** * Add the given cookies to the request. Cookies are always added. * @param cookies the cookies to add @@ -543,6 +592,12 @@ public class MockHttpServletRequestBuilder this.parameters.put(paramName, entry.getValue()); } } + for (Map.Entry> entry : parentBuilder.queryParams.entrySet()) { + String paramName = entry.getKey(); + if (!this.queryParams.containsKey(paramName)) { + this.queryParams.put(paramName, entry.getValue()); + } + } for (Cookie cookie : parentBuilder.cookies) { if (!containsCookie(cookie)) { this.cookies.add(cookie); @@ -632,8 +687,13 @@ public class MockHttpServletRequestBuilder } }); - if (this.url.getRawQuery() != null) { - request.setQueryString(this.url.getRawQuery()); + String query = this.url.getRawQuery(); + if (!this.queryParams.isEmpty()) { + String s = UriComponentsBuilder.newInstance().queryParams(this.queryParams).build().encode().getQuery(); + query = StringUtils.isEmpty(query) ? s : query + "&" + s; + } + if (query != null) { + request.setQueryString(query); } addRequestParams(request, UriComponentsBuilder.fromUri(this.url).build().getQueryParams()); diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java index 22362a33d16..405bfdb46d7 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java @@ -21,6 +21,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.security.Principal; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -234,6 +235,47 @@ public class MockHttpServletRequestBuilderTests { assertThat(request.getParameter("foo[1]")).isEqualTo("baz"); } + @Test + public void queryParameter() { + this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/"); + this.builder.queryParam("foo", "bar"); + this.builder.queryParam("foo", "baz"); + + MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); + + assertThat(request.getParameterMap().get("foo")).isEqualTo(new String[] {"bar", "baz"}); + assertThat(request.getQueryString()).isEqualTo("foo=bar&foo=baz"); + } + + @Test + public void queryParameterMap() { + this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/"); + MultiValueMap queryParams = new LinkedMultiValueMap<>(); + List values = new ArrayList<>(); + values.add("bar"); + values.add("baz"); + queryParams.put("foo", values); + this.builder.queryParams(queryParams); + + MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); + + assertThat(request.getParameterMap().get("foo")).isEqualTo(new String[] {"bar", "baz"}); + assertThat(request.getQueryString()).isEqualTo("foo=bar&foo=baz"); + } + + @Test + public void queryParameterList() { + this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/"); + this.builder.queryParam("foo[0]", "bar"); + this.builder.queryParam("foo[1]", "baz"); + + MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); + + assertThat(request.getQueryString()).isEqualTo("foo%5B0%5D=bar&foo%5B1%5D=baz"); + assertThat(request.getParameter("foo[0]")).isEqualTo("bar"); + assertThat(request.getParameter("foo[1]")).isEqualTo("baz"); + } + @Test public void requestParameterFromQueryWithEncoding() { this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/?foo={value}", "bar=baz");