Unit testing using MockMultipartHttpServletRequest

2019-04-25 07:15发布

I've written a transformer class that takes an HttpServletRequest and transforms it into another type that holds a pointer to the InputStream from the servlet request. (The idea is to abstract the incoming transport protocol from the request handling, so I could also write a similar transformer from FTP, for instance.)

Now I'm trying to write a unit test for this, and I'm having problems. I've managed to figure out the correct boilerplate to create a valid Multipart HTTP request (using the Spring classes MockMultipartHttpServletRequest and MockMultipartFile), but now I get a NullPointerException in the initialize() method of my UploadRequest class. I'm guessing the problem is that somehow the stream inside the MockMultipartHttpServletRequest isn't being initialized correctly, but I can't figure out what I should do differently.

Any suggestions would be gratefully accepted!

This is the stack trace:

java.lang.NullPointerException
    at org.apache.commons.fileupload.MultipartStream$ItemInputStream.makeAvailable(MultipartStream.java:976)
    at org.apache.commons.fileupload.MultipartStream$ItemInputStream.read(MultipartStream.java:886)
    at java.io.InputStream.read(InputStream.java:82)
    at org.apache.commons.fileupload.util.Streams.copy(Streams.java:96)
    at org.apache.commons.fileupload.util.Streams.copy(Streams.java:66)
    at org.apache.commons.fileupload.MultipartStream.readBodyData(MultipartStream.java:592)
    at org.apache.commons.fileupload.MultipartStream.discardBodyData(MultipartStream.java:618)
    at org.apache.commons.fileupload.MultipartStream.skipPreamble(MultipartStream.java:637)
    at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.findNextItem(FileUploadBase.java:984)
    at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:965)
    at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:331)
    at org.apache.commons.fileupload.servlet.ServletFileUpload.getItemIterator(ServletFileUpload.java:148)
    at com.ooyala.UploadRequest.initialize(UploadRequest.java:51)
    at com.ooyala.UploadRequestTest.testCreateFromServletRequest(UploadRequestTest.java:57)

Here's an abbreviated version of my transformer class:

public class UploadRequest {
  private Map<String, String> params;
  private InputStream strIn;
  private Logger Log = Logger.getLogger(UploadRequest.class.getName());

  public UploadRequest()
  {
    params = new HashMap<String, String>();
  }

  public void initialize(HttpServletRequest sRequest, 
                         ServletFileUpload upload)
    throws IOException, FileUploadException
  {
    Enumeration<String> paramNames = sRequest.getParameterNames();
    while (paramNames.hasMoreElements()) {
      String pName = paramNames.nextElement();
      params.put(pName, sRequest.getParameter(pName));
    }
    params.put("request_uri", sRequest.getRequestURI());

    FileItemIterator iter = upload.getItemIterator(sRequest);
    while (iter.hasNext()) {
      FileItemStream item = iter.next();
      try {
        if (!item.isFormField()) {
          // Skip form fields
          params.put("original_file_name", item.getName());
          strIn = item.openStream();
        } 
      } catch (IOException ex) {
        Log.severe("File uploading exception: " + ex.getMessage());
        throw ex;
      }
    }
  }

And here's the unit test:

import org.springframework.mock.web.MockMultipartHttpServletRequest;
import org.springframework.mock.web.MockMultipartFile;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
// etc.... other imports

@RunWith(JMock.class)
public class UploadRequestTest {
  private UploadRequest upRequest;

  @Before
    public void setUp()
    {
      context.setImposteriser(ClassImposteriser.INSTANCE);
      upRequest = new UploadRequest();
    }

  @Test
    public void testCreateFromServletRequest()
      throws IOException, FileUploadException
    {
      String text_contents = "hello world";

      MockMultipartHttpServletRequest sRequest = 
        new MockMultipartHttpServletRequest();
      sRequest.setMethod("POST");
      String boundary = generateBoundary();
      String contentType = "multipart/form-data; boundary="+boundary;
      sRequest.setContentType(contentType);
      sRequest.setRequestURI("/foo");
      sRequest.addParameter("test_param","test_value");
      sRequest.addFile(
        new MockMultipartFile("file1","test_upload.txt","text/plain",
          text_contents.getBytes()));

      ServletFileUpload upload = new ServletFileUpload();
      assertTrue(upload.isMultipartContent(sRequest));

      upRequest.initialize(sRequest, upload);
    }
}

3条回答
Explosion°爆炸
2楼-- · 2019-04-25 07:25

I have the same issue and I googled but no answer. I plugged in the source code from the library, You need to send content, whatever. The library might need to check if it is null in the skip method

MockMultipartHttpServletRequest request
request.setContent("whatever".getBytes());

Posted here for others

查看更多
霸刀☆藐视天下
3楼-- · 2019-04-25 07:29
  1. Add boundary condition
  2. Generate contents as follows

    MockMultipartHttpServletRequest request = 
        this.generateMockMultiPartHttpServletRequest(true);
    MockMultipartFile mockMultipartFile = null;
    try {
        request.setContentType("multipart/form-data; boundary=-----1234");
        request.setCharacterEncoding("text/plain");
        String endline = "\r\n";
        String bondary = "-----1234";
        String textFile = this.encodeTextFile("-----1234", "\r\n", "file","test.csv",
            "text/UTF-8", FileUtils.readFileToString((new File(csvFilePath)), "UTF-8"));
        StringBuilder content = new StringBuilder(textFile.toString());
        content.append(endline);
        content.append(endline);
        content.append(endline);
        content.append("--");
        content.append(bondary);
        content.append("--");
        content.append(endline);
        request.setContent(content.toString().getBytes());
        request.setMethod("POST");
        mockMultipartFile = new MockMultipartFile("file",
        FileUtils.readFileToByteArray(new File(csvFilePath)));
    } catch (Exception e1) {
        e1.printStackTrace();
    }
     request.addFile(mockMultipartFile);
    

Function to encode text

    private String encodeTextFile(String bondary, String endline, String name, 
        String filename, String contentType, String content) {

        final StringBuilder sb = new StringBuilder(64);
        sb.append(endline);
        sb.append("--");
        sb.append(bondary);
        sb.append(endline);
        sb.append("Content-Disposition: form-data; name=\"");
        sb.append(name);
        sb.append("\"; filename=\"");
        sb.append(filename);
        sb.append("\"");
        sb.append(endline);
        sb.append("Content-Type: ");
        sb.append(contentType);
        sb.append(endline);
        sb.append(endline);
        sb.append(content);
        return sb.toString();
    }
查看更多
欢心
4楼-- · 2019-04-25 07:38

I went through the same problem, after searching lot I got this post in which I answered with code that solved my problem.

The Shriprasad's solution works well for text file. But I had some problems with binary files.

https://stackoverflow.com/a/30541653/2762092

查看更多
登录 后发表回答