Testing the File Upload API in Spring Boot

In one of my previous post, I had created an API to upload file. In this post I will write a JUnit test to test the API. We will test the complete flow right from uploading till it is copied to the file system and then we will also see how to mock the FileService class so that the uploaded file is not copied to the file system.

File Upload REST API

@Slf4j
@RestController
@RequestMapping("/api/files")
public class FileUploadAPIController {

  @Autowired FileService fileService;

  @PostMapping("/upload")
  public ResponseEntity<?> handleFileUpload(
    @RequestParam("uploaded-file") List<MultipartFile> uploadedFiles
  ) throws IOException {
    log.debug("Uploaded files size : {}", uploadedFiles.size());
    fileService.copyFile(uploadedFiles);
    return ResponseEntity.ok().build();
  }
}
  • Line 1-4: Declaring a RESTful API with base URL: /api/files
  • Line 6: Injecting the FileService managed bean used for copying the uploaded file to file system
  • Lne 8: Mapping the upload API to be available via HTTP POST to the URL: /api/files/upload
  • Line 10: Accepting more than 1 file uploaded using the request param name uploaded-file
  • Line 12-14: Implementation of the API

Testing the API without mocking FileService

The below code is for testing the file upload API. In this we are going to test the complete flow where a file is sent to the API and then copid to the file system:

@SpringBootTest
@AutoConfigureMockMvc
public class FileUploadAPIControllerE2ETest {
  @Autowired
  MockMvc mockMvc;

  @Value("${app.document-root}")String documentRoot;

  List<Path> filesToBeDeleted = new ArrayList<>();

  @Test
  public void test_handleFileUpload() throws Exception {
    String fileName = "sampleFile.txt";
    MockMultipartFile sampleFile = new MockMultipartFile(
      "uploaded-file",
      fileName, 
      "text/plain",
      "This is the file content".getBytes()
    );

    MockMultipartHttpServletRequestBuilder multipartRequest =
      MockMvcRequestBuilders.multipart("/api/files/upload");

    mockMvc.perform(multipartRequest.file(sampleFile))
      .andExpect(status().isOk());

    Path docRootPath = Path.of(documentRoot, fileName);
    filesToBeDeleted.add(docRootPath);
    assertThat(Files.exists(docRootPath)).isTrue();

  }

  @AfterEach
  public void cleanup() {
    filesToBeDeleted.forEach(path -> {
      try {
        Files.deleteIfExists(path);
      } catch (IOException e) {
        e.printStackTrace();
      }
    });
  }
}
  • Line 1-2: @SpringBootTest annoation creates a mock web server and @AutoConfigureMockMvc configures a @MockMvc object which can be used for invoking the APIs defined in the code
  • Line 7: Injecting the root directoy path where the files are copied. We will use this to validate that the file was actually copied when we invoked the API for testing.
  • Line 9, 33-42: Recording the location of the uploaded files in the file system, so that we can cleanup at the end of each test. This way our tests will be repeatable.
  • Line 13-19: Creating the file to be uploaded. Spring provides an implementation of MultipartFile called MockMultipartFile which can be used for providing files to the API.
  • Line 21-25: Invoking the API by providing the file created before and asserting that the response is HTTP status 200.
  • Line 27-29: Doing the assertion that the file was copied to the required destination.

Testing the API with mock FileService

In this testing we will mock the FileService bean, using @MockBean, which is the bean responsible for copying the uploaded file to the required location in the file system. In this test we will just test the API code without concenring the downstream services it depends on.

@SpringBootTest
@AutoConfigureMockMvc
public class FileUploadAPIControllerTest {
  @MockBean  private FileService fileService;

  @Autowired MockMvc mockMvc;

  @Value("${app.document-root}")String documentRoot;

  @Test
  public void test_handleFileUpload() throws Exception{

    String fileName = "sample-file-mock.txt";
    MockMultipartFile sampleFile = new MockMultipartFile(
        "uploaded-file",
        fileName,
        "text/plain",
        "This is the file content".getBytes());

    MockMultipartHttpServletRequestBuilder multipartRequest =
        MockMvcRequestBuilders.multipart("/api/files/upload");

    mockMvc.perform(multipartRequest.file(sampleFile))
        .andExpect(status().isOk());
  }

  @Test
  public void test_handleFileUpload_NoFileProvided() throws Exception{
    MockMultipartHttpServletRequestBuilder multipartRequest =
        MockMvcRequestBuilders.multipart("/api/files/upload");

    mockMvc.perform(multipartRequest)
        .andExpect(status().isBadRequest());
  }

}
  • Line 4: Mocking the spring managed bean FileService using Mockito via the annotation @MockBean. This annotation is useful to mock spring managed beans

Rest of the code is similar to the one in end to end test previously shown. In this test we dont validate the existence of the file on the file system because the implementation of copying to file system has been mocked and there is no real implementation in the mocked managed bean.

The complete code – API, View and Tests can be found in the Github repo here.



Categories: Spring Boot

Tags: ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: