How to generate exception in Spring MVC test from MockMultipartFile?

Viewed 646

I am trying to write some unit tests for a controller in Spring MVC, and part of the controller method has the follwing code:

        try {
            newProjectFile.setFileType(fileType);
            newProjectFile.setContent(BlobProxy.generateProxy(file.getInputStream(), file.getSize()));
        } catch (Exception e) {
            throw new BadUpdateException(e.getMessage());
        }

I've set up a MockMultipartFile in my unit test, and would like to test the exception case here so that I can get a bad request response.

I've tried setting up something like the following:

unit test:

MockMultipartFile file = new MockMultipartFile("file", "receipts.zip", "application/zip", "".getBytes());

[...]

when(file.getInputStream()).thenThrow(IOException.class);

[...]

and I get the following error:

when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

If I can't use 'when' on a MockMultipartFile like I would any normal mock object, and Mockito doesn't allow you to mock static methods, how can I get an exception to be thrown here?

Edit: as mentioned in the commments, the MockMultipartFile is not from Mockito, hence the error mentioned above.

The question really is how to throw an exception in the try/catch block, which is presumably either by throwing an IOException on file.getInputStream(), or an UnsupportedOperationException on BlobProxy.generateProxy(), so that my method throws the BadUpdateException.

2 Answers

So my colleague found a good way to get around this using an anonymous inner class:

            @Override
            public InputStream getInputStream() throws IOException {
                throw new IOException();
            }
        };

This means that an exception is thrown in the try/catch block in the controller method when trying to get the InputStream from the MockMultipartFile, and the result is the BadUpdateException.

Here is the complete code for uploading an Excel file as multipart file. This is based on M Hall's previous response, so he/she should take credit for it.

This is the controller which will allow you to make the upload:

public class MyController {
    @PostMapping({"/upload"})
    public String upload(@RequestParam("excelFile") MultipartFile excelFile) {
        try {
            //This should throw an IOException
            InputStream in = excelFile.getInputStream();
        } catch (IOException e) {
            //handle exception
        }
        return "redirect:/index";
    }
}

This is how the test should look like:

@SpringBootTest(classes = {MyController.class})
public class MyControllerTest {

        public static final String CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
        public static final byte[] CONTENT = "xml content".getBytes();
        
        //Create a custom mock multipart file. This file will throw an IOException, when the method getInputStream is called.
        CustomMockMultipartFile excelFile = new CustomMockMultipartFile("excelFile", "MyExcelFile.xlsx", CONTENT_TYPE, CONTENT);
        
        @Autowired
        private WebApplicationContext wac;
        
        @Autowired
        MyController myController;
        
        @Test
        public void testUploadIoException() throws Exception {
            MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
            MvcResult result = mockMvc.perform(MockMvcRequestBuilders.multipart("/upload").file(excelFile))
                    .andExpect(redirectedUrl("/index"))
                    .andReturn();
            //Perform other assertions based on your business needs and test specifications
            //Assert that the logic in the catch block is executed
        }
    
        //A private inner class, which extends the MockMultipartFile
        private class CustomMockMultipartFile extends MockMultipartFile {
    
            public CustomMockMultipartFile(String name, String originalFilename, String contentType, byte[] content) {
                super(name, originalFilename, contentType, content);
            }
    
            //Method is overrided, so that it throws an IOException, when it's called
            @Override
            public InputStream getInputStream() throws IOException {
                throw new IOException();
            }
        }
    }
Related