zoukankan      html  css  js  c++  java
  • 使用MockMvc测试Restful接口

    1. MockMvc

    MockMvc是由spring-test包提供,实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,结果的验证十分方便。

    接口MockMvcBuilder,提供一个唯一的build方法,用来构造MockMvc。主要有两个实现:StandaloneMockMvcBuilderDefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。MockMvcBuilders提供了对应的创建方法standaloneSetup方法和webAppContextSetup方法,在使用时直接调用即可。

    2. 引入jar包

    创建SpringBoot项目中默认引入的spring-boot-starter-test间接引入了spring-test,因此无需再额外引入jar包。最终的pom.xml文件如下

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.6.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>mockdemo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>mockdemo</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
                <scope>provided</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-io</artifactId>
                <version>1.3.2</version>
            </dependency>
    
            <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>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    

    3. 创建业务bean和控制器

    User Bean

    package com.example.mockdemo.domain;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    
    import javax.validation.constraints.Past;
    import java.util.Date;
    
    /**
     * @author john
     * @date 2020/4/9 - 18:01
     */
    @Slf4j
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
        private int id;
        private String username;
        private String password;
        @Past(message = "生日必须是过去的时间")
        private Date birthday;
    }
    
    

    FileInfo Dto

    package com.example.mockdemo.dto;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @author john
     * @date 2020/4/10 - 10:00
     */
    @Slf4j
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class FileInfo {
        private String path;
    }
    
    

    UserQueryCondition Dto

    package com.example.mockdemo.dto;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class UserQueryCondition {
        private String username;
        private int age;
        private int ageTo;
        private String xxx;
    }
    
    

    FileController

    package com.example.mockdemo.controller;
    
    import com.example.mockdemo.dto.FileInfo;
    import org.apache.commons.io.IOUtils;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    /**
     * @author john
     * @date 2020/4/10 - 9:54
     */
    @RestController
    @RequestMapping("/file")
    public class FileController {
    
        private String folder = "D:\test";
    
        @PostMapping
        //文件上传
        public FileInfo upload(MultipartFile file) throws Exception {
    
            System.out.println(file.getName());
            System.out.println(file.getOriginalFilename());
            System.out.println(file.getSize());
    
            File localFile = new File(folder, "aa.txt");
    
            file.transferTo(localFile);
    
            return new FileInfo(localFile.getAbsolutePath());
        }
    
        @GetMapping("/{id}")
        //文件下载
        public void download(@PathVariable String id, HttpServletRequest request, HttpServletResponse response) throws Exception {
    
            try (InputStream inputStream = new FileInputStream(new File(folder, id + ".txt"));
                 OutputStream outputStream = response.getOutputStream();) {
    
                response.setContentType("application/x-download");
                response.addHeader("Content-Disposition", "attachment;filename=test.txt");
    
                IOUtils.copy(inputStream, outputStream);
                outputStream.flush();
            }
    
        }
    
    }
    
    

    UserController

    package com.example.mockdemo.controller;
    
    import com.example.mockdemo.domain.User;
    import com.example.mockdemo.dto.UserQueryCondition;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.web.PageableDefault;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @author john
     * @date 2020/4/9 - 18:00
     */
    @RestController
    @RequestMapping("/user")
    public class UserController {
        @PostMapping
        //创建用户
        public User create(@RequestBody User user) {
            user.setId(1);
            return user;
        }
    
        @PutMapping("/{id:\d+}")
        //更新用户
        public User update(@RequestBody User user) {
            user.setId(1);
            return user;
        }
    
        @DeleteMapping("/{id:\d+}")
        //删除用户
        public void delete(@PathVariable String id) {
            System.out.println("用户id为" + id + "删除成功");
        }
    
        @GetMapping
        //查询用户
        public List<User> query(UserQueryCondition condition, @PageableDefault(page = 2, size = 17, sort = "username,asc") Pageable pageable) {
            System.out.println(pageable.getPageSize());
            System.out.println(pageable.getPageNumber());
            System.out.println(pageable.getSort());
            List<User> users = new ArrayList<>();
            users.add(new User(1, "john", "", new Date()));
            users.add(new User(2, "tom", "", new Date()));
            users.add(new User(3, "jim", "", new Date()));
            return users;
        }
    
        @GetMapping("/{id:\d+}")
        //获取具体用户信息
        public User getInfo(@PathVariable String id) {
            System.out.println("进入getInfo服务");
            User user = new User();
            user.setUsername("tom");
            return user;
        }
    }
    
    

    4. 编写测试类

    package com.example.mockdemo;
    
    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.util.Date;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.mock.web.MockMultipartFile;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    class MockdemoApplicationTests {
    
        private MockMvc mockMvc;
    
        @Autowired
        private WebApplicationContext wac;
    
        @BeforeEach
        public void setup() {
            mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
        }
    
        @Test
        //测试删除用户接口
        public void whenDeleteSuccess() throws Exception {
            mockMvc.perform(delete("/user/1")
                    .contentType(MediaType.APPLICATION_JSON_VALUE))
                    .andExpect(status().isOk());
        }
    
        @Test
        //测试更新用户数据
        public void whenUpdateSuccess() throws Exception {
    
            Date date = new Date(LocalDateTime.now().plusYears(1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
            System.out.println(date.getTime());
            String content = "{"id":"1", "username":"tom","password":null,"birthday":" + date.getTime() + "}";
            String reuslt = mockMvc.perform(put("/user/1").contentType(MediaType.APPLICATION_JSON_VALUE)
                    .content(content))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
                    .andExpect(jsonPath("$.id").value("1"))
                    .andReturn().getResponse().getContentAsString();
    
            System.out.println(reuslt);
        }
    
        @Test
        //测试创建用户成功
        public void whenCreateSuccess() throws Exception {
    
            Date date = new Date();
            System.out.println(date.getTime());
            String content = "{"username":"tom","password":null,"birthday":" + date.getTime() + "}";
            String reuslt = mockMvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON_VALUE)
                    .content(content))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.id").value("1"))
                    .andReturn().getResponse().getContentAsString();
    
            System.out.println(reuslt);
        }
    
        @Test
        //测试获取用户信息失败
        public void whenGetInfoFail() throws Exception {
            mockMvc.perform(get("/user/a")
                    .contentType(MediaType.APPLICATION_JSON_VALUE))
                    .andExpect(status().is4xxClientError());
        }
    
        @Test
        //测试获取用户信息成功
        public void whenGetInfoSuccess() throws Exception {
            String result = mockMvc.perform(get("/user/1")
                    .contentType(MediaType.APPLICATION_JSON_VALUE))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.username").value("tom"))
                    .andReturn().getResponse().getContentAsString();
    
            System.out.println(result);
        }
    
        @Test
        //测试查询用户数据接口
        public void whenQuerySuccess() throws Exception {
            String result = mockMvc.perform(
                    get("/user").param("username", "jojo")
                            .param("age", "18")
                            .param("ageTo", "60")
                            .param("xxx", "yyy")
                            .param("size", "15")
                            .param("page", "3")
                            .param("sort", "age,desc")
                            .contentType(MediaType.APPLICATION_JSON_VALUE))
                    .andExpect(status().isOk()).
                            andExpect(jsonPath("$.length()").value(3))
                    .andReturn().getResponse().getContentAsString();
    
            System.out.println(result);
        }
    
        @Test
        //测试文件上传
        public void whenUploadSuccess() throws Exception {
            MockMultipartFile file = new MockMultipartFile("file", "test.txt", "multipart/form-data", "hello upload".getBytes("UTF-8"));
            String result = mockMvc.perform(multipart("/file").file(file))
                    .andExpect(status().isOk())
                    .andReturn().getResponse().getContentAsString();
            System.out.println(result);
        }
    
    }
    
    

    5. 代码分享

    微云下载

    6. 参考

    Server-Side Tests

    Spring MVC测试框架详解——服务端测试

    官方示例

    Java后端安全开发Spring Security开发REST服务

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 交换Easy
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法提高 多项式输出
    Java实现 蓝桥杯VIP 算法训练 矩阵乘方
    QT中给各控件增加背景图片(可缩放可旋转)的几种方法
    回调函数实现类似QT中信号机制
    std::string的Copy-on-Write:不如想象中美好(VC不使用这种方式,而使用对小字符串更友好的SSO实现)
  • 原文地址:https://www.cnblogs.com/ifme/p/12671773.html
Copyright © 2011-2022 走看看