2

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.

M Hall
  • 51
  • 5
  • 1
    A `MockMultipartFile` isn't a mock defined by `mockito`. It's a spring class. Maybe you can add some more context to your question, so we can see if there isn't a different way to achieve the same result. – second Mar 25 '20 at 10:48
  • OK thanks, and good point. I'll take a look. – M Hall Mar 25 '20 at 10:50
  • Try to add the complete method and your test. Ideally a [mre]. A solution might be the use of `PowerMockito` to mock the static method, but there might be better approaches. But finding them depends on the code you have and test you have written. – second Mar 25 '20 at 11:02

2 Answers2

2

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.

M Hall
  • 51
  • 5
0

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();
            }
        }
    }