• 如何对Spring MVC中的Controller进行单元测试


    对Controller进行单元测试是Spring框架原生就支持的能力,它可以模拟HTTP客户端发起对服务地址的请求,可以不用借助于诸如Postman这样的外部工具就能完成对接口的测试。
    具体来讲,是由Spring框架中的spring-test模块提供的实现,详见MockMvc

    如下将详细阐述如何使用MockMvc测试框架实现对“Spring Controller”进行单元测试,基于Spring Boot开发框架进行验证。
    添加测试框架依赖:

    <!-- Spring框架 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    !<-- Spring测试框架 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- 文件操作工具 -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
    

    导入静态工具方法

    为了便于在编写测试用例时直接调用测试框架自带的静态方法,首先需要导入这些静态工具方法。
    需要导入的静态方法如下:

    import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
    import static org.springframework.test.web.servlet.setup.SharedHttpSessionConfigurer.*;
    

    初始化MockMvc

    初始化MockMvc有2种方式:
    方式1:明确指定需要测试的“Controller”类进行配置
    方式2:基于Spring容器进行配置,包含了Spring MVC环境和所有“Controller”类,通常使用这种方式。

    @SpringBootTest
    public class TestControllerTest {
    
        MockMvc mockMvc;
    
        // 初始化MockMvc
        @BeforeEach
        void setUp(WebApplicationContext wac) {
            // 方式1:明确指定需要测试的“Controller”类
            this.mockMvc = MockMvcBuilders.standaloneSetup(new TestController()).build();
    
            // 方式2:基于Spring容器进行配置,包含了Spring MVC环境和所有“Controller”类。
            this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
        }
    }
    

    另外,还可以对MockMvc进行全局配置。

    // 全局配置MockMvc
    this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
            .defaultRequest(get("/").accept(MediaType.APPLICATION_JSON)) // 默认请求路径
            .apply(sharedHttpSession()) // 配置session
            .alwaysExpect(status().isOk()) // 预期响应状态码
            .alwaysExpect(content().contentType("application/json;charset=UTF-8")) // 预期内容类型
            .build();
    

    执行测试

    MockMvc支持对常见的HTTP方法,如:GET,POST,PUT,DELETE等,甚至还支持文件上传请求。

    测试GET接口

    // 访问GET接口:不带参数
    @Test
    public void testSimpleGet() throws Exception {
        MvcResult result = this.mockMvc.perform(get("/test/simple/get")
                .accept(MediaType.APPLICATION_JSON)) // 接受JSON格式响应消息
                .andReturn(); // 获取返回结果
        Assertions.assertEquals("OK", result.getResponse().getContentAsString());
    }
    
    // 访问GET接口:带URL参数
    @Test
    public void testParamGet() throws Exception {
        int id = 10;
        // 方式1:在URI模板中指定参数
        //MvcResult result = this.mockMvc.perform(get("/test/param/get?id={id}", id).accept(MediaType.APPLICATION_JSON)).andReturn();
    
        // 方式2:通过param()方法指定参数
        //MvcResult result = this.mockMvc.perform(get("/test/param/get").param("id", String.valueOf(id)).accept(MediaType.APPLICATION_JSON)).andReturn();
    
        // 方式3:通过queryParam()方法指定参数
        MvcResult result = this.mockMvc.perform(get("/test/param/get").queryParam("id", String.valueOf(id)).accept(MediaType.APPLICATION_JSON)).andReturn();
        Assertions.assertEquals("OK: " + id, result.getResponse().getContentAsString());
    }
    

    测试POST接口

    // 传递表单参数
    @Test
    public void testSimplePost() throws Exception {
        int id = 10;
    
        // 调用param()方法传递参数
        MvcResult result = this.mockMvc.perform(post("/test/simple/post")
                .param("id", String.valueOf(id))
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .accept(MediaType.APPLICATION_JSON))
                .andReturn();
        Assertions.assertEquals("{\"id\":10}", result.getResponse().getContentAsString());
    }
    
    // 传递JSON参数
    @Test
    public void testSimplePostJson() throws Exception {
        // 调用content()方法传递json字符串参数
        Subject subject = new Subject();
        subject.setId(10);
        String content = JSON.toJSONString(subject);
        MvcResult result = this.mockMvc.perform(post("/test/simple/post/json")
                .content(content)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andReturn();
        Assertions.assertEquals("{\"id\":10}", result.getResponse().getContentAsString());
    }
    

    测试文件上传

    @Test
    public void testFileUploadSingle() throws Exception {
        File file = new File("C:\\Users\\xxx\\Downloads\\test.jpg");
        String fileName = FilenameUtils.getName(file.getName());
        byte[] bytes = FileUtils.readFileToByteArray(file);
        MockMultipartFile mockMultipartFile = new MockMultipartFile("file", fileName, MediaType.MULTIPART_FORM_DATA_VALUE, bytes);
        this.mockMvc.perform(multipart("/test/upload/single").file(mockMultipartFile))
                    .andExpect(status().isOk())
                    .andExpect(content().string("OK"))
                    .andDo(print());
    }
    

    定义预期结果

    断言响应结果时,有2种方式:
    1.使用JUnit提供的Assert断言工具判断返回结果,这是一种非常普遍和常见的方式
    2.在MockMvc框架中可以通过andExpect()方法定义一个或多个预期结果,当其中一个期望结果断言失败时,就不会断言其他期望值了

    // 使用Junit断言工具判断返回结果是否符合预期
    @Test
    public void testAssertResult() throws Exception {
        MvcResult result = this.mockMvc.perform(get("/test/simple/get").accept(MediaType.APPLICATION_JSON)).andDo(print()).andReturn();
        Assert.assertEquals("OK", result.getResponse().getContentAsString());
    }
    
    // 在MockMvc框架中定义预期结果
    @Test
    public void testExpectations() throws Exception {
        this.mockMvc.perform(get("/test/simple/get").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())        // 预期响应状态码为200
                .andExpect(content().string("OK")) // 预期返回值为字符串“OK”
                .andDo(print());
    }
    

    相比于使用Junit的断言工具判断返回结果,在MockMvc框架中直接定义预期结果进行断言检查更加简洁。

    写在最后

    使用Spring提供的测试框架MockMvc可以非常方便地实现对HTTP服务接口进行单元测试,不要把基础的功能验证工作都交给测试童鞋,应该通过单元测试来保证代码迭代的稳定性。

    【参考】
    https://blog.csdn.net/coolcoffee168/article/details/88638042 springboot 单元测试 (controller层) 方法 -- MockMvc

  • 相关阅读:
    【微机接口】中断系统:PC系列机中的中断系统
    Three.js对模型进行多区域染色
    如何拥有自己的专属GPT-本地部署目前最强大模型llama3
    [LeetCode周赛复盘] 第 310 场周赛20220911
    【机器学习】机器学习基本概念/术语3
    MybatisPlus 一些技巧
    spring使用@Value标签读取.properties文件的中文乱码问题的解决
    第10讲:Vue组件的定义与注册
    Maven私服仓库配置-Nexus详解
    iOS 类方法和对象方法的区别
  • 原文地址:https://www.cnblogs.com/nuccch/p/15901976.html