When testing an application using HtmlUnit, its often useful to use HtmlFileInput.setData(...) instead of HtmlFileInput.setFiles(...) in order to not have to generate temporary files. However, when using MockMvcWebClientBuilder, this doesn't work. The problem is that HtmlUnitRequestBuilder ignores the data part.

24926 added support for file uploads in HtmlUnitRequestBuilder, but missed this use case.

Test case:

filesubmit.html:

<!DOCTYPE html>
<html>
<head>
<title>File submission test</title>
</head>
<body>
<form method="POST" action="/filesubmit" enctype="multipart/form-data">
    File: <input name="file" type="file"/>
    <button type="submit">Submit</button>
</form>
</body>
</html>

FileSubmitController.java:

@Controller
public class FileSubmitController {
    @PostMapping("/filesubmit")
    public @ResponseBody String handleFileSubmit(@RequestParam(name = "file", required = false) MultipartFile file) {
        return file == null ? "unset" : file.getOriginalFilename();
    }
}

FileSubmitTest.java:

@WebMvcTest(FileSubmitController.class)
public class FileSubmitTest {
    @Autowired
    WebClient webClient;

    @Test
    void testFileSubmitUsingFile() throws FailingHttpStatusCodeException, MalformedURLException, IOException {
        HtmlPage page = webClient.getPage("/filesubmit.html");
        HtmlFileInput file = page.querySelector("input[type=file]");
        file.setContentType("application/json");
        file.setFiles(new File("src/test/resources/test.json"));
        HtmlButton submit = page.querySelector("button");
        Page resultPage = submit.click();
        // Succeeds
        assertEquals("test.json", resultPage.getWebResponse().getContentAsString());
    }

    @Test
    void testFileSubmitUsingData() throws FailingHttpStatusCodeException, MalformedURLException, IOException {
        HtmlPage page = webClient.getPage("/filesubmit.html");
        HtmlFileInput file = page.querySelector("input[type=file]");
        file.setValueAttribute("test.json");
        file.setContentType("application/json");
        file.setData("{}".getBytes());
        HtmlButton submit = page.querySelector("button");
        Page resultPage = submit.click();
        // Fails with expected: <test.json> but was: <unset>
        assertEquals("test.json", resultPage.getWebResponse().getContentAsString());
    }
}

When starting the application normally and changing WebClient from a mocked version to a normal version using http://localhost:8080/ both tests succeed.

I'm guessing in HtmlUnitRequestBuilder.params(...), the else part should be something like this:

                else { // use data
                    part = new MockPart(pair.getName(), pair.getValue(), pair.getData());
                    if (pair.getMimeType() == null)
                        part.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
                    else
                        part.getHeaders().setContentType(MediaType.valueOf(pair.getMimeType()));
                }

I'm not sure if there is a specific edge case to use the old "mimic empty file upload" else case.

Tested using Spring Boot 2.5.2 using Spring WebMVC 5.3.8.

Comment From: sbrannen

I'm not sure if there is a specific edge case to use the old "mimic empty file upload" else case.

I think that use case will still exist, resulting in 3 use cases.

  1. file is set
  2. data is set
  3. neither file nor data is set (empty upload)

Comment From: sbrannen

Related issues:

  • 24926

  • 26799

Comment From: sbrannen

I'm not sure if there is a specific edge case to use the old "mimic empty file upload" else case.

Well, after diving into the implementation, it turns out you were right about that. 👍

I was able to support both of those use cases in a single else block. See 8a7c4fc10d60168ebded312e2b02b3a052c46d33 for details.

I would be grateful if you could try out the fix in one of the upcoming 5.3.10 snapshots and let us know if everything works as expected.

Cheers!

Comment From: svschouw-bb

I would be grateful if you could try out the fix in one of the upcoming 5.3.10 snapshots and let us know if everything works as expected.

I'd love to, but I'm not sure how. Where and when will these snapshots be available?

Comment From: sbrannen

I'd love to, but I'm not sure how. Where and when will these snapshots be available?

The 5.3.10 snapshots for spring-test are available here.

Information on how to consume snapshots can be found here.

Comment From: svschouw-bb

Yes, seems to work! Great!

Comment From: sbrannen

Yes, seems to work! Great!

Awesome!

Thanks for letting us know.