SpringBoot的测试环境搭建
文章目录
前言
这里来讲讲我个人进行SprigBoot测试环境搭建的学习和踩坑,方便我们单间个人测试案例,对关键的接口进行测试保障。减少后期的运维成本。
搭建基本环境
Jar包的引入
- SpringBoot的核心依赖
- Json工具,这里我还是使用简单的Hutools
<!-- 核心Web包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.8</version>
</dependency>
<!-- 测试包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
建立我们的测试接口
主要用于测试
package com.example.basic.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.basic.entity.DictBed;
import com.example.basic.service.IDictBedService;
import com.example.basic.utils.resultbean.ErrorCodeInfo;
import com.example.basic.utils.resultbean.ResultBean;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import java.util.List;
/**
* @author YeZhiyue
* Description 床类型字典表 服务实现类
* Date 2020/10/01
* Time 16:35
* Mail 739153436@qq.com
*/
@Component
@RestController
@RequestMapping("/dictBed")
public class DictBedController {
@Resource
private IDictBedService iDictBedService;
/**
* @description 新增
* @author YeZhiyue
* @email 739153436@qq.com
* @date 2020/10/01 16:35
*/
@ApiOperation("新增")
@PutMapping("/admin/insert")
public ResultBean<Boolean> adminInsert(@RequestBody List<DictBed> pDictBedList) {
return ResultBean.restResult(iDictBedService.adminInsert(pDictBedList), ErrorCodeInfo.CREATED);
}
/**
* @description 删除
* @author YeZhiyue
* @email 739153436@qq.com
* @date 2020/10/01 16:35
*/
@ApiOperation("删除")
@DeleteMapping("/admin/delete")
public ResultBean<Boolean> adminDelete(@NotEmpty(message = "删除的id列表不能为空") @RequestBody List<String> pIdList) {
return ResultBean.restResult(iDictBedService.adminDelete(pIdList), ErrorCodeInfo.NO_CONTENT);
}
/**
* @description 分页查询
* @author YeZhiyue
* @email 739153436@qq.com
* @date 2020/10/01 16:35
*/
@ApiOperation("分页查询")
@GetMapping("/admin/page")
public ResultBean<Page<DictBed>> adminPage(@Min(value = -1, message = "页码过小") int pPageNum, int pPageSize, @RequestBody DictBed pDictBed) {
return ResultBean.restResult(iDictBedService.adminPage(pPageNum, pPageSize, pDictBed), ErrorCodeInfo.OK);
}
/**
* @description 列表查询
* @author YeZhiyue
* @email 739153436@qq.com
* @date 2020/10/01 16:35
*/
@ApiOperation("分页查询")
@GetMapping("/admin/list")
public ResultBean<List<DictBed>> adminList() {
return ResultBean.restResult(iDictBedService.list(), ErrorCodeInfo.OK);
}
/**
* @description 更新
* @author YeZhiyue
* @email 739153436@qq.com
* @date 2020/10/01 16:35
*/
@ApiOperation("更新")
@PostMapping("/admin/update")
public ResultBean<Boolean> adminUpdate(@Validated(value = DictBed.Update.class) @RequestBody List<DictBed> pDictBedList) {
return ResultBean.restResult(iDictBedService.adminUpdate(pDictBedList), ErrorCodeInfo.OK);
}
}
建立我们的测试类
MockMVC 介绍
MockMvc是由spring-test包提供,实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,结果的验证十分方便。
接口MockMvcBuilder,提供一个唯一的build方法,用来构造MockMvc。主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。MockMvcBuilders提供了对应的创建方法standaloneSetup方法和webAppContextSetup方法,在使用时直接调用即可。
参考博客,下面这篇博客可以参考一下
使用示例
简单示例测试类
import cn.hutool.json.JSONUtil;
import com.example.basic.entity.DictBed;
import org.junit.Before;
import org.junit.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.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.Collections;
/**
* @author 叶之越
* Description
* Date 2020/10/24
* Time 20:02
* Mail 739153436@qq.com
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class MockMvcRestful {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext wac;
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); //构建MockMVC
}
/**
* 说明:基本请求操作方法
* 1、mockMvc.perform执行一个请求。
* 2、MockMvcRequestBuilders.get("XXX")构造一个请求。
* 3、ResultActions.param添加请求传值
* 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
* 5、ResultActions.andExpect添加执行完成后的断言。
* 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
* 比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
* 7、ResultActions.andReturn表示执行完成后返回相应的结果。
*/
@Test
public void insert() throws Exception {
DictBed build = new DictBed();
build.setCnName("测试床");
build.setDescription("测试床描述");
build.setEnName("TestBed");
String requestJson = JSONUtil.toJsonStr(Collections.singleton(build));
String responseString = mockMvc.perform(MockMvcRequestBuilders
.put("/dictBed/admin/insert")
// 设置参数是Json类型,也就是 @RequestBody
.contentType(MediaType.APPLICATION_JSON)
.content(requestJson)
)
// 打印中间信息
.andDo(MockMvcResultHandlers.print())
// 异常判断
.andExpect(MockMvcResultMatchers.status().isOk())
// 返回值
.andReturn().getResponse().getContentAsString();
System.out.println(responseString);
}
/**
* 请求参数既包含 @RequestBody 和 @RequestParam
*/
@Test
public void page() throws Exception {
DictBed build = new DictBed();
build.setCnName("测试床");
build.setDescription("测试床描述");
build.setEnName("TestBed");
String requestJson = JSONUtil.toJsonStr(build);
String responseString = mockMvc.perform(MockMvcRequestBuilders
.get("/dictBed/admin/page")
// 设置参数是Json类型,也就是 @RequestBody
.contentType(MediaType.APPLICATION_JSON)
.content(requestJson)
// 参数是普通的RequestParam
.param("pPageNum","0")
.param("pPageSize","10")
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
)
// 打印中间信息
.andDo(MockMvcResultHandlers.print())
// 异常判断
.andExpect(MockMvcResultMatchers.status().isOk())
// 返回值
.andReturn().getResponse().getContentAsString();
System.out.println(responseString);
}
@Test
public void getUser() throws Exception {
String responseString = mockMvc.perform(MockMvcRequestBuilders.get("/dictBed/admin/list")).andReturn().getResponse().getContentAsString();
System.out.println("result : " + responseString);
}
}
测试一
MockHttpServletRequest:
HTTP Method = PUT
Request URI = /dictBed/admin/insert
Parameters = {}
Headers = [Content-Type:"application/json", Content-Length:"75"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.example.basic.controller.DictBedController
Method = com.example.basic.controller.DictBedController#adminInsert(List)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"code":201,"msg":"Created","data":true}
Forwarded URL = null
Redirected URL = null
Cookies = []
{"code":201,"msg":"Created","data":true}
测试二
MockHttpServletRequest:
HTTP Method = GET
Request URI = /dictBed/admin/page
Parameters = {pPageNum=[0], pPageSize=[10]}
Headers = [Content-Type:"application/json", Accept:"application/json;charset=UTF-8", Content-Length:"73"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.example.basic.controller.DictBedController
Method = com.example.basic.controller.DictBedController#adminPage(int, int, DictBed)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json;charset=UTF-8"]
Content type = application/json;charset=UTF-8
Body = {"code":200,"msg":"OK","data":{"records":[{"id":"1319998035661230082","weight":0,"createTime":1603547030737,"updateTime":0,"createBy":null,"updateBy":null,"version":0,"deleted":0,"extra":"","tenantId":"","cnName":"测试床","enName":"TestBed","description":"测试床描述"},{"id":"1319997802151768065","weight":0,"createTime":1603546975065,"updateTime":0,"createBy":null,"updateBy":null,"version":0,"deleted":0,"extra":"","tenantId":"","cnName":"测试床","enName":"TestBed","description":"测试床描述"},{"id":"1319989345302429698","weight":0,"createTime":1603544958796,"updateTime":0,"createBy":null,"updateBy":null,"version":0,"deleted":0,"extra":"","tenantId":"","cnName":"测试床","enName":"TestBed","description":"测试床描述"}],"total":3,"size":10,"current":1,"orders":[],"optimizeCountSql":true,"hitCount":false,"countId":null,"maxLimit":null,"searchCount":true,"pages":1}}
Forwarded URL = null
Redirected URL = null
Cookies = []
补充(断言、MockMVC其他请求方式)
示例
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* @author 叶之越
* Description
* Date 2020/10/24
* Time 20:02
* Mail 739153436@qq.com
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class MockMvcSupplement {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext wac;
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); //构建MockMVC
}
/**
* 断言方法补充
*/
@Test
public void assertSupplement() {
// void assertEquals(boolean expected, boolean actual):检查两个变量或者等式是否平衡
// void assertTrue(boolean expected, boolean actual):检查条件为真
// void assertFalse(boolean condition):检查条件为假
// void assertNotNull(Object object):检查对象不为空
// void assertNull(Object object):检查对象为空
// void assertSame(boolean condition):assertSame() 方法检查两个相关对象是否指向同一个对象
// void assertNotSame(boolean condition):assertNotSame() 方法检查两个相关对象是否不指向同一个对象
// void assertArrayEquals(expectedArray, resultArray):assertArrayEquals() 方法检查两个数组是否相等
}
/**
* MockMVC 其他测试接口方法示例补充
*/
@Test
public void supplement() {
String jsonStr = "{\"username\":\"Dopa\",\"passwd\":\"ac3af72d9f95161a502fd326865c2f15\",\"status\":\"1\"}";
// mockMvc.perform(MockMvcRequestBuilders.get("/hello?name={name}","mrbird"));
// mockMvc.perform(MockMvcRequestBuilders.post("/user/{id}", 1));
// mockMvc.perform(MockMvcRequestBuilders.fileUpload("/fileupload").file("file", "文件内容".getBytes("utf-8")));
// mockMvc.perform(MockMvcRequestBuilders.get("/hello").param("message", "hello"));
// mockMvc.perform(MockMvcRequestBuilders.get("/hobby/save").param("hobby", "sleep", "eat"));
// MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
// params.add("name", "mrbird");
// params.add("hobby", "sleep");
// params.add("hobby", "eat");
// mockMvc.perform(MockMvcRequestBuilders.get("/hobby/save").params(params));
// mockMvc.perform(MockMvcRequestBuilders.get("/index").sessionAttr(name, value));
// mockMvc.perform(MockMvcRequestBuilders.get("/index").cookie(new Cookie(name, value)));
// mockMvc.perform(MockMvcRequestBuilders.get("/index").contentType(MediaType.APPLICATION_JSON_UTF8));
// mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1).accept(MediaType.APPLICATION_JSON));
// mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1).header(name, values));
// mockMvc.perform(MockMvcRequestBuilders.get("/index"))
// .andDo(MockMvcResultHandlers.print());
}
}
踩坑
坑点一:搭建我们的MockMVC环境
刚开始我使用测试的时候一直报java.lang.NullPointerException的错误,于是找了网上的一篇博客,介绍了两种引入MockMVC的方式,第一种方式有点问题就是引入Controller对象的引入方式不正确需要注意一下,链接如下
[Springboot mockMVC 测试错误 java.lang.NullPointerException](Springboot mockMVC 测试错误 java.lang.NullPointerException)
这里直接介绍我比较推荐的MockMVC引入方法,直接集成Web环境,不需要再单独引入Controller,比较方便
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class WebApplicationTests {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext wac;
@Before
public void setup(){
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); //构建MockMVC
}
@Test
public void getUser() throws Exception {
String responseString = mockMvc.perform(MockMvcRequestBuilders.post("/getUser")).andReturn().getResponse().getContentAsString();
System.out.println("result : "+responseString);
}
}
坑点二:引入MockMVC的引入方式错误
在坑点一中的博客中的第一种独立安装MockMVC的方法是有问题的,不能直接通过new的方式来传入MockMVC,这样是会报错的
util.NestedServletException: Request processing failed; nested exception is NullPointerException
解决方案我就放在下面的博客链接中,其实就是改变了Controller的注入方式为@Autowire引入
[成功解决util.NestedServletException: Request processing failed; nested exception is NullPointerException](成功解决util.NestedServletException: Request processing failed; nested exception is NullPointerException)
坑点三:我们使用MockMVC进行返回值测试的时候中文会乱码
- 这里我们就需要设置接收返回值的类型的编码,通常我们会设置成Jason类型,但是不要忘记设置字符编码,通常设置为 UTF-8