使用spring的MockMvc编写自动化测试用例,提高接口测试通过率,提高自身代码质量,减少常见BUG出现。
废话不多说直接上代码。
maven依赖:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>com.demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-test-demo-1</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.6</version>
<exclusions>
<exclusion>
<artifactId>jsqlparser</artifactId>
<groupId>com.github.jsqlparser</groupId>
</exclusion>
<exclusion>
<artifactId>mybatis-spring</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
<exclusion>
<artifactId>mybatis</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- Mysql Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
</dependency>
<!-- /Mysql Driver -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.44</version>
</dependency>
</dependencies>
</project>
实体类代码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.demo.entity;
import java.io.Serializable;
public class ResultResponse<T> implements Serializable {
public static final Integer SUCCESS_CODE = 0;
private static final long serialVersionUID = -6557898536448299915L;
private Integer code;
private String message;
private T data;
public ResultResponse() {
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((code == null) ? 0 : code.hashCode());
result = prime * result + ((data == null) ? 0 : data.hashCode());
result = prime * result + ((message == null) ? 0 : message.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ResultResponse other = (ResultResponse) obj;
if (code == null) {
if (other.code != null)
return false;
} else if (!code.equals(other.code))
return false;
if (data == null) {
if (other.data != null)
return false;
} else if (!data.equals(other.data))
return false;
if (message == null) {
if (other.message != null)
return false;
} else if (!message.equals(other.message))
return false;
return true;
}
@Override
public String toString() {
return "ResultResponse [code=" + code + ", message=" + message + ", data=" + data + "]";
}
}
user实体类:
package com.demo.entity;
import java.io.Serializable;
public class UserDemo implements Serializable {
private static final long serialVersionUID = -1L;
private Integer id;
private String userName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((userName == null) ? 0 : userName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
UserDemo other = (UserDemo) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (userName == null) {
if (other.userName != null)
return false;
} else if (!userName.equals(other.userName))
return false;
return true;
}
@Override
public String toString() {
return "UserDemo [id=" + id + ", userName=" + userName + "]";
}
}
controller的代码:
package com.demo.controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.demo.entity.ResultResponse;
import com.demo.entity.UserDemo;
@RestController
@RequestMapping("/demo")
public class DemoController {
@GetMapping("/getOne/{id}")
public ResultResponse<UserDemo> getOne(@PathVariable Integer id) {
UserDemo userDemo = new UserDemo();
userDemo.setId(id);
userDemo.setUserName("测试");
ResultResponse<UserDemo> resultResponse = new ResultResponse<UserDemo>();
resultResponse.setCode(200);
resultResponse.setMessage("success");
resultResponse.setData(userDemo);
return resultResponse;
}
@PostMapping(value = "/editOne",consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResultResponse<Boolean> editOne(@RequestBody UserDemo userDemo) {
System.out.println("要编辑客户:"+userDemo);
ResultResponse<Boolean> resultResponse = new ResultResponse<Boolean>();
resultResponse.setCode(200);
resultResponse.setMessage("success");
resultResponse.setData(null);
return resultResponse;
}
}
test下的测试用例代码:
package com.demo.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.demo.SpringTestDemoApplication;
import com.demo.entity.ResultResponse;
import com.demo.entity.UserDemo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* @author DaXiong
* @create 2020-02-21 9:09
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {SpringTestDemoApplication.class})
public class DemoControllerTest {
@Resource
private WebApplicationContext webAC;
private MockMvc mockMvc;
private static final String URI = "/demo";
@Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webAC).build();
}
@Test
// 执行测试要执行的数据库脚本(脚本位置: src/test/resources/sql/DemoControllerTest/testGetOne.sql)
@Sql(scripts = {"classpath:sql/DemoControllerTest/testGetOne.sql"})
// 事务回滚,方法执行完后,把刚开始执行脚本的数据恢复
@Rollback(true)
// 开始事务才会回滚,不然每次执行数据库脚本插入重复数据
@Transactional
public void testGetOne() throws Exception {
int id = 1;
MvcResult mvcResult = mockMvc.perform(get(URI + "/getOne/{id}",id)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.header("key1", "消息头的值")) // 如果请求要有特定的消息头可以在此添加
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.code", is(200)))
.andExpect(jsonPath("$.message", containsString("success")))
.andExpect(jsonPath("$.data.userName", is("测试")))
.andDo(print())
.andReturn();
String contentAsString = mvcResult.getResponse().getContentAsString();
ResultResponse response = JSONObject.parseObject(contentAsString, ResultResponse.class);
UserDemo userDemo = JSONObject.parseObject(JSON.toJSONString(response.getData()),UserDemo.class);
//如果不能通过接口返回值判断是否成功,例如新增接口
// 可以在这里通过调用查询接口,查询是否查到自己调用接口后新增的客户,通过一下比较是否与自己
// 新增的一致,一致则接口成功
// 值为空则报错,用于检验不能为null的情况
assertNotNull(userDemo);
assertEquals(Integer.valueOf(id),userDemo.getId());
}
@Test
// 如果该测试用例不需要执行脚本,则将该注解注释即可
// @Sql(scripts = {"classpath:sql/DemoControllerTest/testEditOne.sql"})
@Rollback(true)
@Transactional
public void testEditOne() throws Exception {
UserDemo userDemo = new UserDemo();
userDemo.setId(2);
userDemo.setUserName("编辑测试");
MvcResult mvcResult = mockMvc.perform(post(URI + "/editOne").contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.content(JSON.toJSONString(userDemo)))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.code", is(200)))
.andExpect(jsonPath("$.message", containsString("success")))
.andExpect(jsonPath("$.data", nullValue()))
.andDo(print())
.andReturn();
}
}
执行后的效果图:
写完接口后,写测试用例是一个好习惯!