spring-bootrestjunit5web-testingmockmvc

How to create unit tests with MockMvc for your REST controller


I have a REST controller and I want to create Unit tests for my controller and I'm not interested in loading the spring context.

Having a spring-boot application, the requirement is to use junit5 and MockMvc.


Solution

  • Here is a working example with MockMvc, creating simple unit tests for the REST controller.

    Here is the Dictionary REST controller.

    @Slf4j
    @RequiredArgsConstructor
    @RestController
    @RequestMapping(DICTIONARY_ENDPOINT_URL)
    public class DictionaryController {
        protected static final String DICTIONARY_ENDPOINT_URL = "/dictionaries";
    
        private final DictionaryService dictionaryService;
    
        @GetMapping(value = "download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
        public ResponseEntity<byte[]> downloadDictionariesFile() throws FileNotFoundException {
            log.info("Called download dictionaries file endpoint.");
            final String fileRelativePath = dictionaryService.getDictionariesFileRelativePath();
    
            return ResponseEntity.ok()
                    .header("Content-Disposition", "attachment; filename=" + getDictionariesFileByRelativePath(fileRelativePath))
                    .body(dictionaryService.getDictionaryFileContent(fileRelativePath));
        }
    }
    

    Here is the DictionaryControllerTest

    package <...>;
    
    import <...>.DictionaryService;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.junit.jupiter.MockitoExtension;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    
    import java.io.FileNotFoundException;
    
    import static org.hamcrest.Matchers.containsString;
    import static org.mockito.ArgumentMatchers.anyString;
    import static org.mockito.Mockito.*;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    @ExtendWith(MockitoExtension.class)
    class DictionaryControllerTest {
    
        private String dictionaryFilePath = "<<path to the file>>";
        private String dictionaryFileContent = "<<content here>>";
        private String errorMessage = "Dictionary file, does not exists!";
        private String endpointUrl = "/dictionaries/download";
    
        @Mock
        private DictionaryService dictionaryService;
    
        @InjectMocks
        private DictionaryController dictionaryController;
        
        private MockMvc mockMvc;
    
        @BeforeEach
        public void setUp() {
            mockMvc = MockMvcBuilders.standaloneSetup(dictionaryController).build();
        }
    
        @Test
        @DisplayName("Should return response with content file")
        void downloadDictionariesFile_validRequest_success() throws Exception {
            when(dictionaryService.getDictionariesFileRelativePath())
                    .thenReturn(dictionaryFilePath);
            when(dictionaryService.getDictionaryFileContent(anyString()))
                    .thenReturn(dictionaryFileContent.getBytes());
                    
            mockMvc.perform(get(endpointUrl))
                    .andExpect(status().isOk())
                    .andExpect(content().string(containsString(dictionaryFileContent)));
            
            verify(dictionaryService).getDictionariesFileRelativePath();
            verify(dictionaryService).getDictionaryFileContent(any());
        }
    }
    

    Here are a few lines of my pom.xml file

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.1</version>
        <relativePath/>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>