如何使用 MockMvc 为 REST 控制器创建单元测试

How to create unit tests with MockMvc for your REST controller

我有一个 REST 控制器,我想为我的控制器创建 Unit tests,但我对加载 spring 上下文不感兴趣。

有一个spring-boot应用程序,要求是使用junit5和MockMvc。

这是一个使用 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>