10

Clearly I'm not using this test fixture right. My servlet works just fine in tomcat, but when I try to use this mock, the multi-part boundary is not found. "the request was rejected because no multipart boundary was found".

There is an answer here that shows how to use this using a text file, but that answer sets the boundary string explicitly and embeds the file as test. I would think I would not need to do with by hand with methods like mockrequest.addFile(...)

What am I not setting here or how I am doing this wrong?

@org.testng.annotations.Test
public void testDoPost() throws Exception
{
    MockMultipartFile file = new MockMultipartFile("test.zip", "test.zip", "application/zip", MyServletTest.class.getResourceAsStream("/test.zip"));
    MockMultipartHttpServletRequest mockRequest = new MockMultipartHttpServletRequest();
    mockRequest.addFile(file);
    mockRequest.set
    mockRequest.setMethod("POST");
    mockRequest.setParameter("variant", "php");
    mockRequest.setParameter("os", "mac");
    mockRequest.setParameter("version", "3.4");
    MockHttpServletResponse response = new MockHttpServletResponse();
    new MyServletTest().doPost(mockRequest, response);
    //  BOOM !
}

Here is the exception

Caused by: blablah:   the request was rejected because no multipart boundary was found
Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
marathon
  • 7,881
  • 17
  • 74
  • 137

5 Answers5

21

You need to set the boundary.

Here there is a good explanations about what is the boundary https://stackoverflow.com/a/10932533/2762092

To solve your problem try this code.

    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;

    import org.apache.commons.lang.ArrayUtils;
    import org.springframework.mock.web.MockHttpServletResponse;
    import org.springframework.mock.web.MockMultipartFile;
    import org.springframework.mock.web.MockMultipartHttpServletRequest;


public class FileUploadTest {

    public void testDoPost() throws IOException {
            Path path = Paths.get("c:\\temp\\test.zip");
            byte[] data = Files.readAllBytes(path);
            MockMultipartFile file = new MockMultipartFile("test.zip", "test.zip",
                    "application/zip", data);
            MockMultipartHttpServletRequest mockRequest = new MockMultipartHttpServletRequest();
            String boundary = "q1w2e3r4t5y6u7i8o9";
            mockRequest.setContentType("multipart/form-data; boundary="+boundary);
            mockRequest.setContent(createFileContent(data,boundary,"application/zip","test.zip"));
            mockRequest.addFile(file);
            mockRequest.setMethod("POST");
            mockRequest.setParameter("variant", "php");
            mockRequest.setParameter("os", "mac");
            mockRequest.setParameter("version", "3.4");
            MockHttpServletResponse response = new MockHttpServletResponse();
            new FileUpload().doPost(mockRequest, response);
        }

        public byte[] createFileContent(byte[] data, String boundary, String contentType, String fileName){
            String start = "--" + boundary + "\r\n Content-Disposition: form-data; name=\"file\"; filename=\""+fileName+"\"\r\n"
                     + "Content-type: "+contentType+"\r\n\r\n";;

            String end = "\r\n--" + boundary + "--"; // correction suggested @butfly 
            return ArrayUtils.addAll(start.getBytes(),ArrayUtils.addAll(data,end.getBytes()));
        }
}
Samuel
  • 1,149
  • 8
  • 25
  • 1
    Is there any way to do this without manually inserting the boundary characters in the file? Most HTTP clients will do this kind of thing for you (curl, for instance), so I'm surprised that some combination of `MockMultipartHttpServletRequest` and `MockMultipartFile` doesn't get you this functionality for free. – kashev Apr 01 '22 at 19:58
  • @kashev This is a good question. At that time I didn't find any method that did this automatically. It may be that in the new Spring releases there is already such a feature, after all this post is already 7 years old. I will try to find out about it. – Samuel Apr 05 '22 at 15:36
3

Vote up for Samuel. Though spent one day trying to make it working. The problem was in:

String end = "--" + boundary + "--";

Should be:

String end = "\r\n--" + boundary + "--";
Anton Kushch
  • 223
  • 2
  • 8
2

Great answer for Samuel, but a bug:

        String end = "\r\n"+ boundary + "--";

should be:

        String end = "--"+ boundary + "--";

Thanks for his work very much.

butfly
  • 105
  • 1
  • 6
  • 2
    In fact it should be: String end = "--"+ boundary + "--"; – butfly Jun 22 '15 at 19:50
  • @Samuel Glad for your reply but please update your code again to remove the leading "\r\n" in end suffix string, or it will be appended to the original content of part, so that parsing like a integer form part causes exception. – butfly Jun 25 '15 at 01:51
0

Able to add multiple fields ,

   private byte[] createFileContents(String requestId, String date, String image, String invoiceNumber,String imageFile) {

    String requestIdField = "--" + BOUNDARY + "\r\n Content-Disposition: form-data; name=\"" + REQUEST_ID_KEY
            + "\";" + "Content-type: " + CONTENT_TYPE + "\r\n value=\"12345\"" + "\r\n\r\n";
    String requestIdValue = requestId + "\r\n";
    String numberFiledField = "--" + BOUNDARY + "\r\n Content-Disposition: form-data; name=\"" + NUMBER_KEY + "\";"
            + "Content-type: " + CONTENT_TYPE + "\r\n value=\"12345\"" + "\r\n\r\n";
    String invoiceValue = invoiceNumber + "\r\n";
    String dateField = "--" + BOUNDARY + "\r\n Content-Disposition: form-data; name=\"" + DATE_KEY + "\";"
            + "Content-type: " + CONTENT_TYPE + "\r\n value=\"12345\"" + "\r\n\r\n";
    String dateValue = date + "\r\n";
    String imageField = "--" + BOUNDARY + "\r\n Content-Disposition: form-data; name=\"" + IMAGE_KEY
            + "\"; filename=\"" + imageFile + "\"\r\n" + "Content-type: " + CONTENT_TYPE + "\r\n\r\n";
    String imageValue = image + "\r\n";
    String end = "\r\n--" + BOUNDARY + "--";
    return ArrayUtils.addAll((requestIdField + requestIdValue + numberFiledField + invoiceValue + dateField
            + dateValue + imageField + imageValue).getBytes(), ArrayUtils.addAll(data, end.getBytes()));
}
Rajaneesh
  • 181
  • 1
  • 12
0

My way to test multipart/form-data via mock MVC sending params and files is the following:

    @Test
void testSendFeedback() throws Exception {
    var builder = MockMvcRequestBuilders.multipart(URL_PATH);

    Path path = Files.createTempFile("test-file", "tmp");
    builder = builder.part(new MockPart("image", path.toFile().getName(), Files.readAllBytes(path)));

    builder.param("field1", "value1")
        .param("fields2", "value2");

    mockMvc.perform(builder.header(HttpHeaders.AUTHORIZATION, YOUR_AUTH_VALUE).contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
        .andDo(print())
        .andExpect(status().isNoContent());
}
silver_mx
  • 828
  • 9
  • 16