Как создавать модульные тесты с помощью MockMvc для вашего контроллера REST

#spring-boot #rest #junit5 #web-testing #mockmvc

#spring-boot #rest #junit5 #веб-тестирование #mockmvc

Вопрос:

У меня есть контроллер REST, и я хочу создать Unit tests его для своего контроллера, и я не заинтересован в загрузке контекста spring.

При наличии приложения с весенней загрузкой требуется использовать junit5 и MockMvc.

Ответ №1:

Вот рабочий пример с MockMvc, создающий простые модульные тесты для контроллера REST.

Вот контроллер Dictionary REST.

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

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

Вот несколько строк моего pom.xml файла

 <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>