diff --git a/spring-test/spring-test.gradle b/spring-test/spring-test.gradle
index 99b051667b0..231ee2e405c 100644
--- a/spring-test/spring-test.gradle
+++ b/spring-test/spring-test.gradle
@@ -41,6 +41,7 @@ dependencies {
optional("org.xmlunit:xmlunit-matchers")
optional("org.skyscreamer:jsonassert")
optional("com.jayway.jsonpath:json-path")
+ optional("commons-fileupload:commons-fileupload")
optional("org.jetbrains.kotlin:kotlin-reflect")
optional("org.jetbrains.kotlin:kotlin-stdlib")
optional("io.projectreactor:reactor-test")
@@ -74,7 +75,6 @@ dependencies {
testCompile("org.hsqldb:hsqldb")
testCompile("org.apache.httpcomponents:httpclient")
testCompile("io.projectreactor.netty:reactor-netty")
- testCompile("commons-fileupload:commons-fileupload")
testCompile("de.bechte.junit:junit-hierarchicalcontextrunner")
testRuntime("org.junit.vintage:junit-vintage-engine") {
exclude group: "junit", module: "junit"
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java
index f26449fc110..e01663f77b3 100644
--- a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java
+++ b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2020 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.
@@ -19,6 +19,8 @@ package org.springframework.test.web.client.match;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
@@ -26,16 +28,23 @@ import javax.xml.transform.dom.DOMSource;
import org.hamcrest.Matcher;
import org.w3c.dom.Node;
+import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.util.JsonExpectationsHelper;
import org.springframework.test.util.XmlExpectationsHelper;
import org.springframework.test.web.client.RequestMatcher;
+import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
+import org.springframework.util.StreamUtils;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.springframework.test.util.AssertionErrors.assertEquals;
@@ -159,10 +168,69 @@ public class ContentRequestMatchers {
}
/**
- * Access to request body matchers. Matches content type {@link MediaType#MULTIPART_FORM_DATA}
+ * Parse the body as multipart data and assert it contains exactly the
+ * values from the given {@code MultiValueMap}. Values may be of type:
+ *
+ * - {@code String} - form field
+ *
- {@link Resource} - content from a file
+ *
- {@code byte[]} - other raw content
+ *
+ * Note: This method uses the Apache Commons File Upload
+ * library to parse the multipart data and it must be on the test classpath.
+ * @param expectedMap the expected multipart values
+ * @since 5.3
*/
- public MultipartFormDataRequestMatchers multipart() {
- return new MultipartFormDataRequestMatchers();
+ public RequestMatcher multipartData(MultiValueMap expectedMap) {
+ return multipartData(expectedMap, true);
+ }
+
+ /**
+ * Variant of {@link #multipartData(MultiValueMap)} that does the same but
+ * only for a subset of the actual values.
+ * @param expectedMap the expected multipart values
+ * @since 5.3
+ */
+ public RequestMatcher multipartDataContains(Map expectedMap) {
+ MultiValueMap map = new LinkedMultiValueMap<>(expectedMap.size());
+ expectedMap.forEach(map::add);
+ return multipartData(map, false);
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ private RequestMatcher multipartData(MultiValueMap expectedMap, boolean assertSize) {
+ return request -> {
+ MultiValueMap actualMap = MultipartHelper.parse(request);
+ if (assertSize) {
+ assertEquals("Multipart request content: " + actualMap, expectedMap.size(), actualMap.size());
+ }
+ for (Map.Entry> entry : expectedMap.entrySet()) {
+ String name = entry.getKey();
+ List> values = entry.getValue();
+ assertTrue("No Multipart '" + name + "'", actualMap.get(name) != null);
+ assertTrue("Multipart value count " + values.size(), assertSize ?
+ values.size() == actualMap.get(name).size() :
+ values.size() <= actualMap.get(name).size());
+ for (int i = 0; i < values.size(); i++) {
+ Object expected = values.get(i);
+ Object actual = actualMap.get(name).get(i);
+ if (expected instanceof Resource) {
+ expected = StreamUtils.copyToByteArray(((Resource) expected).getInputStream());
+ }
+ if (expected instanceof byte[]) {
+ assertTrue("Multipart is not a file", actual instanceof MultipartFile);
+ assertEquals("Multipart content", expected, ((MultipartFile) actual).getBytes());
+ }
+ else if (expected instanceof String) {
+ assertTrue("Multipart is not a String", actual instanceof String);
+ assertEquals("Multipart content", expected, (String) actual);
+ }
+ else {
+ throw new IllegalArgumentException(
+ "Unexpected multipart value type: " + expected.getClass());
+ }
+ }
+ }
+ };
}
/**
@@ -268,4 +336,32 @@ public class ContentRequestMatchers {
protected abstract void matchInternal(MockClientHttpRequest request) throws Exception;
}
+
+ private static class MultipartHelper {
+
+ public static MultiValueMap parse(ClientHttpRequest request) {
+ MultipartHttpServletRequest servletRequest = adaptToMultipartRequest(request);
+ MultiValueMap result = new LinkedMultiValueMap<>();
+ for (Map.Entry> entry : servletRequest.getMultiFileMap().entrySet()) {
+ for (MultipartFile value : entry.getValue()) {
+ result.add(entry.getKey(), value);
+ }
+ }
+ for (Map.Entry entry : servletRequest.getParameterMap().entrySet()) {
+ for (String value : entry.getValue()) {
+ result.add(entry.getKey(), value);
+ }
+ }
+ return result;
+ }
+
+ private static MultipartHttpServletRequest adaptToMultipartRequest(ClientHttpRequest request) {
+ MockClientHttpRequest source = (MockClientHttpRequest) request;
+ MockHttpServletRequest target = new MockHttpServletRequest();
+ target.setContent(source.getBodyAsBytes());
+ source.getHeaders().forEach((name, values) -> values.forEach(v -> target.addHeader(name, v)));
+ return new CommonsMultipartResolver().resolveMultipart(target);
+ }
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/MultipartFormDataRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/MultipartFormDataRequestMatchers.java
deleted file mode 100644
index 74beeac7775..00000000000
--- a/spring-test/src/main/java/org/springframework/test/web/client/match/MultipartFormDataRequestMatchers.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright 2002-2019 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.client.match;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import org.apache.commons.io.IOUtils;
-import org.hamcrest.Matcher;
-import org.hamcrest.Matchers;
-import org.jetbrains.annotations.NotNull;
-
-import org.springframework.core.io.Resource;
-import org.springframework.http.MediaType;
-import org.springframework.http.client.ClientHttpRequest;
-import org.springframework.mock.http.client.MockClientHttpRequest;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.test.web.client.RequestMatcher;
-import org.springframework.util.Assert;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.multipart.MultipartHttpServletRequest;
-import org.springframework.web.multipart.commons.CommonsMultipartResolver;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.springframework.test.util.AssertionErrors.assertEquals;
-import static org.springframework.test.util.AssertionErrors.fail;
-
-/**
- * Factory for assertions on the multipart form data parameters. Handles only {@link MediaType#MULTIPART_FORM_DATA}
- *
- * An instance of this class is typically accessed via {@link ContentRequestMatchers#multipart()}
- *
- * @author Valentin Spac
- * @since 5.3
- */
-public class MultipartFormDataRequestMatchers {
-
- public RequestMatcher param(String parameter, String... expectedValues) {
- List> matcherList = Arrays.stream(expectedValues)
- .map(Matchers::equalTo)
- .collect(Collectors.toList());
-
- return this.param(parameter, matcherList);
- }
-
- @SafeVarargs
- @SuppressWarnings("varargs")
- public final RequestMatcher param(String parameter, Matcher super String>... matchers) {
- return this.param(parameter, Arrays.stream(matchers).collect(Collectors.toList()));
- }
-
- public RequestMatcher param(String parameter, Matcher> matchers) {
- return request -> {
- Map requestParams = MultipartRequestParser.parameterMap(request);
- assertValueCount(parameter, requestParams, 1);
-
- String[] values = requestParams.get(parameter);
- assertThat("Request parameter [" + parameter + "]", Arrays.asList(values), matchers);
- };
- }
-
- private RequestMatcher param(String parameter, List> matchers) {
- return request -> {
- Map requestParams = MultipartRequestParser.parameterMap(request);
- assertValueCount(parameter, requestParams, matchers.size());
-
- String[] values = requestParams.get(parameter);
-
- Assert.state(values != null, "No values for request parameter " + parameter);
- for (int i = 0; i < matchers.size(); i++) {
- assertThat("Request parameter [" + parameter + "]", values[i], matchers.get(i));
- }
- };
- }
-
- public RequestMatcher params(MultiValueMap expectedParameters) {
- return request -> {
- Map requestParams = MultipartRequestParser.parameterMap(request);
-
- expectedParameters.forEach((param, values) -> {
- String[] actualValues = requestParams.get(param);
- Assert.state(actualValues != null, "No values for request parameter " + param);
-
- assertValueCount(param, requestParams, values.size());
-
- assertEquals("Parameter " + param, values, Arrays.asList(actualValues));
- });
- };
- }
-
- public RequestMatcher file(String parameter, byte[]... resources) {
- return request -> {
- MultiValueMap files = MultipartRequestParser.multiFileMap(request);
-
- assertValueCount(parameter, files, resources.length);
-
- assertByteArrayMatch(parameter, Arrays.asList(resources), files.get(parameter));
- };
- }
-
- @SafeVarargs
- @SuppressWarnings("varargs")
- public final RequestMatcher file(String parameter, Matcher super Resource>... matchers) {
- return request -> {
- MultiValueMap files = MultipartRequestParser.multiFileMap(request);
- assertValueCount(parameter, files, matchers.length);
- List parts = files.get(parameter);
-
- for (int i = 0; i < matchers.length; i++) {
- assertThat("File [" + parameter + "]", parts.get(i).getResource(), matchers[i]);
- }
- };
- }
-
- public RequestMatcher file(String parameter, Resource... resources) {
- return request -> {
- MultiValueMap files = MultipartRequestParser.multiFileMap(request);
- assertValueCount(parameter, files, resources.length);
-
- assertResourceMatch(parameter, Arrays.asList(resources), files.get(parameter));
- };
- }
-
- public RequestMatcher files(MultiValueMap expectedFiles) {
- return request -> {
- MultiValueMap actualFiles = MultipartRequestParser.multiFileMap(request);
-
- expectedFiles.forEach((param, parts) -> {
- assertValueCount(param, actualFiles, parts.size());
- assertResourceMatch(param, parts, actualFiles.get(param));
- });
- };
- }
-
-
- private void assertByteArrayMatch(String parameterName, List expectedFiles,
- List actualFiles) {
- for (int index = 0; index < actualFiles.size(); index++) {
- MultipartFile multiPartFile = actualFiles.get(index);
- byte[] expectedContent = expectedFiles.get(index);
-
- try {
- assertEquals("Content mismatch for file " + parameterName, expectedContent,
- multiPartFile.getBytes());
- }
- catch (IOException ex) {
- throw new AssertionError("Could not get bytes from actual multipart files", ex);
- }
- }
- }
-
- private void assertResourceMatch(String parameterName, List expectedFiles,
- List actualFiles) {
- for (int index = 0; index < actualFiles.size(); index++) {
- MultipartFile multiPartFile = actualFiles.get(index);
- Resource expectedResource = expectedFiles.get(index);
- try {
- byte[] fileContent = IOUtils.toByteArray(expectedResource.getInputStream());
-
- assertEquals("Content mismatch for file " + parameterName, fileContent,
- multiPartFile.getBytes());
- assertEquals("Filename ", expectedResource.getFilename(), multiPartFile.getOriginalFilename());
- }
- catch (IOException ex) {
- throw new AssertionError("Could not get bytes from actual multipart files", ex);
- }
- }
- }
-
- private static void assertValueCount(String parameter, Map map, int count) {
- String[] values = map.get(parameter);
- if (values == null) {
- fail("Expected <" + parameter + "> to exist but was null");
- }
- assertValueCount(parameter, count, Arrays.asList(values));
- }
-
- private static void assertValueCount(String parameter, MultiValueMap map, int count) {
- List> values = map.get(parameter);
- assertValueCount(parameter, count, values);
- }
-
- private static void assertValueCount(String parameter, int count, List> values) {
- String message = "Expected multipart file <" + parameter + ">";
- if (count > values.size()) {
- fail(message + " to have at least <" + count + "> values but found " + values.size());
- }
- }
-
-
- private static class MultipartRequestParser {
- private static MultipartHttpServletRequest extract(ClientHttpRequest request) {
- MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
- final MockHttpServletRequest mockHttpServletRequest = toMockHttpServletRequest(mockRequest);
-
- CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
- return multipartResolver.resolveMultipart(mockHttpServletRequest);
- }
-
- @NotNull
- private static MockHttpServletRequest toMockHttpServletRequest(MockClientHttpRequest mockRequest) {
- final MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();
- mockHttpServletRequest.setContent(mockRequest.getBodyAsBytes());
-
- // copy headers
- mockRequest.getHeaders()
- .forEach((headerName, headerValue) ->
- headerValue.forEach(value -> mockHttpServletRequest.addHeader(headerName, value)));
- return mockHttpServletRequest;
- }
-
- private static Map parameterMap(ClientHttpRequest request) {
- return extract(request).getParameterMap();
- }
-
- private static MultiValueMap multiFileMap(ClientHttpRequest request) {
- return extract(request).getMultiFileMap();
- }
- }
-}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/match/MultipartFormDatRequestMatchersTests.java b/spring-test/src/test/java/org/springframework/test/web/client/match/MultipartFormDatRequestMatchersTests.java
deleted file mode 100644
index cdeaaa29cec..00000000000
--- a/spring-test/src/test/java/org/springframework/test/web/client/match/MultipartFormDatRequestMatchersTests.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright 2002-2019 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.client.match;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Arrays;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-import org.jetbrains.annotations.NotNull;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.core.io.Resource;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpOutputMessage;
-import org.springframework.http.MediaType;
-import org.springframework.http.converter.FormHttpMessageConverter;
-import org.springframework.mock.http.client.MockClientHttpRequest;
-import org.springframework.mock.web.MockMultipartFile;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-
-import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.equalTo;
-
-/**
- * Unit tests for {@link MultipartFormDataRequestMatchers}.
- *
- * @author Valentin Spac
- */
-public class MultipartFormDatRequestMatchersTests {
-
- private MockClientHttpRequest request = new MockClientHttpRequest();
- private MultipartFormDataRequestMatchers multipartRequestMatchers = MockRestRequestMatchers.content().multipart();
-
- @BeforeEach
- public void setUp() {
- this.request.getHeaders().setContentType(MediaType.MULTIPART_FORM_DATA);
- }
-
- @Test
- public void testContains() throws Exception {
- MultiValueMap payload = new LinkedMultiValueMap<>();
- payload.add("foo", "bar");
- payload.add("foo", "baz");
- payload.add("lorem", "ipsum");
-
- writeForm(payload);
-
- multipartRequestMatchers.param("foo", containsInAnyOrder("bar", "baz")).match(request);
- }
-
- @Test
- public void testNoContains() throws Exception {
- MultiValueMap payload = new LinkedMultiValueMap<>();
- payload.add("foo", "bar");
- payload.add("foo", "baz");
- payload.add("lorem", "ipsum");
-
- writeForm(payload);
-
- assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
- multipartRequestMatchers.param("foo", containsInAnyOrder("wrongValue")).match(request));
- }
-
- @Test
- public void testEqualMatcher() throws Exception {
- MultiValueMap payload = new LinkedMultiValueMap<>();
- payload.add("foo", "bar");
- payload.add("baz", "foobar");
-
- writeForm(payload);
- multipartRequestMatchers.param("foo", equalTo("bar")).match(request);
- }
-
- @Test
- public void testNoEqualMatcher() throws Exception {
- MultiValueMap payload = new LinkedMultiValueMap<>();
- payload.add("foo", "bar");
- payload.add("baz", "foobar");
-
- writeForm(payload);
-
- assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
- multipartRequestMatchers.param("foo", equalTo("wrongValue")).match(request));
- }
-
- @Test
- public void testParamMatch() throws Exception {
- MultiValueMap payload = new LinkedMultiValueMap<>();
- payload.add("foo", "bar");
- payload.add("baz", "foobar");
-
- writeForm(payload);
- multipartRequestMatchers.param("foo", "bar").match(request);
- }
-
- @Test
- public void testParamNoMatch() throws Exception {
- MultiValueMap payload = new LinkedMultiValueMap<>();
- payload.add("foo", "bar");
- payload.add("baz", "foobar");
-
- writeForm(payload);
- assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
- multipartRequestMatchers.param("foo", "wrongValue").match(request));
- }
-
- @Test
- public void testParamsMultimapMatch() throws Exception {
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("foo", "value 1");
- map.add("bar", "value A");
- map.add("baz", "value B");
-
- writeForm(map);
-
- multipartRequestMatchers.params(map).match(this.request);
- }
-
- @Test
- public void testParamsMultimapNoMatch() throws Exception {
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("foo", "foo value");
- map.add("bar", "bar value");
- map.add("baz", "baz value");
- map.add("baz", "second baz value");
-
- writeForm(map);
-
- map.set("baz", "wrong baz value");
- assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
- multipartRequestMatchers.params(map).match(this.request));
- }
-
-
- @Test
- public void testResourceMatch() throws Exception {
- MockMultipartFile foo = new MockMultipartFile("fooFile", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
- MockMultipartFile bar = new MockMultipartFile("fooFile", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
- MockMultipartFile foobar = new MockMultipartFile("foobarFile", "foobar.txt", "text/plain", "Foobar Lorem ipsum".getBytes());
-
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("fooParam", "foo value");
- map.add("barParam", "bar value");
- map.add(foo.getName(), foo.getResource());
- map.add(bar.getName(), bar.getResource());
- map.add(foobar.getName(), foobar.getResource());
-
- writeForm(map);
-
- multipartRequestMatchers.file(foo.getName(), foo.getResource(), bar.getResource()).match(this.request);
- }
-
- @Test
- public void testResourceNoMatch() throws Exception {
- MockMultipartFile foo = new MockMultipartFile("fooFile", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
- MockMultipartFile bar = new MockMultipartFile("barFile", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
-
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("fooParam", "foo value");
- map.add("barParam", "bar value");
- map.add(foo.getName(), foo.getResource());
- map.add(bar.getName(), bar.getResource());
-
- writeForm(map);
-
-
- assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
- multipartRequestMatchers.file(foo.getName(), foo.getResource(), bar.getResource()).match(this.request));
- }
-
- @Test
- public void testByteArrayMatch() throws Exception {
- MockMultipartFile foo = new MockMultipartFile("fooFile", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
- MockMultipartFile bar = new MockMultipartFile("fooFile", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
- MockMultipartFile foobar = new MockMultipartFile("foobarFile", "foobar.txt", "text/plain", "Foobar Lorem ipsum".getBytes());
-
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("fooParam", "foo value");
- map.add("barParam", "bar value");
- map.add(foo.getName(), foo.getResource());
- map.add(bar.getName(), bar.getResource());
- map.add(foobar.getName(), foobar.getResource());
-
- writeForm(map);
-
- multipartRequestMatchers.file(foo.getName(), foo.getBytes(), bar.getBytes()).match(this.request);
- }
-
- @Test
- public void testByteArrayNoMatch() throws Exception {
- MockMultipartFile foo = new MockMultipartFile("fooFile", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
- MockMultipartFile bar = new MockMultipartFile("barFile", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
-
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("fooParam", "foo value");
- map.add("barParam", "bar value");
- map.add(foo.getName(), foo.getResource());
- map.add(bar.getName(), bar.getResource());
-
- writeForm(map);
-
-
- assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
- multipartRequestMatchers.file(foo.getName(), bar.getBytes()).match(this.request));
- }
-
- @Test
- public void testResourceMatcher() throws Exception {
- MockMultipartFile foo = new MockMultipartFile("fooFile", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
- MockMultipartFile bar = new MockMultipartFile("barFile", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
-
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("fooParam", "foo value");
- map.add("barParam", "bar value");
- map.add(foo.getName(), foo.getResource());
- map.add(bar.getName(), bar.getResource());
-
- writeForm(map);
- multipartRequestMatchers.file(foo.getName(), resourceMatcher(foo.getResource())).match(this.request);
- }
-
- @Test
- public void testResourceMatcherNoMatch() throws Exception {
- MockMultipartFile foo = new MockMultipartFile("fooFile", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
- MockMultipartFile bar = new MockMultipartFile("barFile", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
-
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("fooParam", "foo value");
- map.add("barParam", "bar value");
- map.add(foo.getName(), foo.getResource());
- map.add(bar.getName(), bar.getResource());
-
- writeForm(map);
- assertThatExceptionOfType(AssertionError.class).isThrownBy(() ->
- multipartRequestMatchers.file(foo.getName(), resourceMatcher(bar.getResource())).match(this.request));
- }
-
- @NotNull
- private Matcher resourceMatcher(Resource expectedResource) {
- return new TypeSafeMatcher() {
-
- @Override
- public void describeTo(Description description) {
- description.appendValue(expectedResource.getDescription());
- }
-
- @Override
- protected boolean matchesSafely(Resource resource) {
- try {
- byte[] actual = IOUtils.toByteArray(resource.getInputStream());
- byte[] expected = IOUtils.toByteArray(expectedResource.getInputStream());
-
- return StringUtils.equals(expectedResource.getFilename(), resource.getFilename())
- && Arrays.equals(expected, actual);
- }
- catch (IOException e) {
- throw new RuntimeException("Could not read resource content");
- }
- }
- };
- }
-
- @Test
- public void testResourceMultimapMatch() throws Exception {
- MockMultipartFile foo = new MockMultipartFile("fooFile", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
- MockMultipartFile bar = new MockMultipartFile("barFile", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
-
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("fooParam", "foo value");
- map.add("barParam", "bar value");
- map.add(foo.getName(), foo.getResource());
- map.add(bar.getName(), bar.getResource());
-
- writeForm(map);
-
- MultiValueMap files = new LinkedMultiValueMap<>();
- files.add(foo.getName(), foo.getResource());
- files.add(bar.getName(), bar.getResource());
-
- multipartRequestMatchers.files(files).match(this.request);
- }
-
-
- private void writeForm(MultiValueMap payload) throws IOException {
- FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
- formHttpMessageConverter.write(payload, MediaType.MULTIPART_FORM_DATA, new HttpOutputMessage() {
- @Override
- public OutputStream getBody() throws IOException {
- return MultipartFormDatRequestMatchersTests.this.request.getBody();
- }
-
- @Override
- public HttpHeaders getHeaders() {
- return MultipartFormDatRequestMatchersTests.this.request.getHeaders();
- }
- });
- }
-}
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/match/MultipartRequestMatchersTests.java b/spring-test/src/test/java/org/springframework/test/web/client/match/MultipartRequestMatchersTests.java
new file mode 100644
index 00000000000..9f70ea77980
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/web/client/match/MultipartRequestMatchersTests.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2002-2020 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.client.match;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.FormHttpMessageConverter;
+import org.springframework.mock.http.client.MockClientHttpRequest;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.multipart.MultipartFile;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+/**
+ * Unit tests for
+ * {@link ContentRequestMatchers#multipartData(MultiValueMap)} and.
+ * {@link ContentRequestMatchers#multipartDataContains(Map)}.
+ *
+ * @author Valentin Spac
+ * @author Rossen Stoyanchev
+ */
+public class MultipartRequestMatchersTests {
+
+ private final MockClientHttpRequest request = new MockClientHttpRequest();
+
+ private final MultiValueMap input = new LinkedMultiValueMap<>();
+
+ private final MultiValueMap expected = new LinkedMultiValueMap<>();
+
+
+ @BeforeEach
+ public void setUp() {
+ this.request.getHeaders().setContentType(MediaType.MULTIPART_FORM_DATA);
+ }
+
+
+ @Test
+ public void testContains() throws Exception {
+ this.input.add("foo", "bar");
+ this.input.add("foo", "baz");
+ this.input.add("lorem", "ipsum");
+
+ this.expected.add("foo", "bar");
+
+ writeAndAssertContains();
+ }
+
+ @Test
+ public void testDoesNotContain() {
+ this.input.add("foo", "bar");
+ this.input.add("foo", "baz");
+ this.input.add("lorem", "ipsum");
+
+ this.expected.add("foo", "wrongValue");
+
+ assertThatExceptionOfType(AssertionError.class).isThrownBy(this::writeAndAssert);
+ }
+
+ @Test
+ public void testParamsMatch() throws Exception {
+ this.input.add("foo", "value 1");
+ this.input.add("bar", "value A");
+ this.input.add("baz", "value B");
+
+ this.expected.addAll(this.input);
+
+ writeAndAssert();
+ }
+
+ @Test
+ public void testResourceMatch() throws Exception {
+ MultipartFile f1 = new MockMultipartFile("f1", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
+ MultipartFile f2 = new MockMultipartFile("f2", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
+ MultipartFile f3 = new MockMultipartFile("f3", "foobar.txt", "text/plain", "Foobar Lorem ipsum".getBytes());
+
+ this.input.add("fooParam", "foo value");
+ this.input.add("barParam", "bar value");
+ this.input.add(f1.getName(), f1.getResource());
+ this.input.add(f2.getName(), f2.getResource());
+ this.input.add(f3.getName(), f3.getResource());
+
+ this.expected.addAll(this.input);
+
+ writeAndAssert();
+ }
+
+ @Test
+ public void testResourceNoMatch() {
+ MockMultipartFile foo = new MockMultipartFile("f1", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
+ MockMultipartFile bar = new MockMultipartFile("f2", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
+
+ this.input.add("fooParam", "foo value");
+ this.input.add("barParam", "bar value");
+ this.input.add(foo.getName(), foo.getResource());
+ this.input.add(bar.getName(), bar.getResource());
+
+ this.expected.addAll(this.input);
+ this.expected.set(foo.getName(), bar.getResource());
+
+ assertThatExceptionOfType(AssertionError.class).isThrownBy(this::writeAndAssert);
+ }
+
+ @Test
+ public void testByteArrayMatch() throws Exception {
+ MultipartFile f1 = new MockMultipartFile("f1", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
+ MultipartFile f2 = new MockMultipartFile("f2", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
+ MultipartFile f3 = new MockMultipartFile("f3", "foobar.txt", "text/plain", "Foobar Lorem ipsum".getBytes());
+
+ this.input.add("fooParam", "foo value");
+ this.input.add("barParam", "bar value");
+ this.input.add(f1.getName(), f1.getResource());
+ this.input.add(f2.getName(), f2.getResource());
+ this.input.add(f3.getName(), f3.getResource());
+
+ this.expected.addAll(this.input);
+ this.expected.set(f1.getName(), f1.getBytes());
+ this.expected.set(f2.getName(), f2.getBytes());
+ this.expected.set(f3.getName(), f3.getBytes());
+
+ writeAndAssert();
+ }
+
+ @Test
+ public void testByteArrayNoMatch() throws Exception {
+ MultipartFile f1 = new MockMultipartFile("f1", "foo.txt", "text/plain", "Foo Lorem ipsum".getBytes());
+ MultipartFile f2 = new MockMultipartFile("f2", "bar.txt", "text/plain", "Bar Lorem ipsum".getBytes());
+
+ this.input.add("fooParam", "foo value");
+ this.input.add("barParam", "bar value");
+ this.input.add(f1.getName(), f1.getResource());
+ this.input.add(f2.getName(), f2.getResource());
+
+ this.expected.addAll(this.input);
+ this.expected.set(f1.getName(), f2.getBytes());
+
+ assertThatExceptionOfType(AssertionError.class).isThrownBy(this::writeAndAssert);
+ }
+
+
+ private void writeAndAssert() throws IOException {
+ writeForm();
+ new ContentRequestMatchers().multipartData(this.expected).match(request);
+ }
+
+ private void writeAndAssertContains() throws IOException {
+ writeForm();
+ Map expectedMap = this.expected.toSingleValueMap();
+ new ContentRequestMatchers().multipartDataContains(expectedMap).match(request);
+ }
+
+ private void writeForm() throws IOException {
+ new FormHttpMessageConverter().write(this.input, MediaType.MULTIPART_FORM_DATA,
+ new HttpOutputMessage() {
+ @Override
+ public OutputStream getBody() throws IOException {
+ return MultipartRequestMatchersTests.this.request.getBody();
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ return MultipartRequestMatchersTests.this.request.getHeaders();
+ }
+ });
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartFile.java b/spring-web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartFile.java
index 54298a0a882..691b52519e2 100644
--- a/spring-web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartFile.java
+++ b/spring-web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartFile.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2020 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.
@@ -232,4 +232,11 @@ public class CommonsMultipartFile implements MultipartFile, Serializable {
}
}
+ @Override
+ public String toString() {
+ return "MultipartFile[field=\"" + this.fileItem.getFieldName() + "\"" +
+ (this.fileItem.getName() != null ? ", filename=" + this.fileItem.getName() : "" ) +
+ (this.fileItem.getContentType() != null ? ", contentType=" + this.fileItem.getContentType() : "") +
+ ", size=" + this.fileItem.getSize() + "]";
+ }
}