用Spring Boot编写RESTful API 学习笔记

概念

驱动模块

被测模块

桩模块

  • 替代尚未开发完毕的子模块
  • 替代对环境依赖较大的子模块 (例如数据访问层)

示例

测试 Service

@RunWith(SpringRunner.class)
@SpringBootTest
public class TvSeriesServiceTest { @MockBean
TvSeriesDao tvSeriesDao;
@MockBean
TvCharacterDao tvCharacterDao; @Autowired
TvSeriesService tvSeriesService; @Test
public void testGetAll() { List<TvSeries> list = new ArrayList<>();
TvSeries ts = new TvSeries();
ts.setName("POI");
list.add(ts); // 告诉 mock 当执行 getAll 方法时,返回上面创建的 list
Mockito.when(tvSeriesDao.getAll()).thenReturn(list); List<TvSeries> result = tvSeriesService.getAllTvSeries(); Assert.assertTrue(result.size() == list.size());
Assert.assertTrue("POI".equals(result.get(0).getName()));
} @Test
public void testGetOne() {
//根据不同的传入参数,被 mock 的 bean 返回不同的数据的例子
String newName = "Person Of Interest";
BitSet mockExecuted = new BitSet();
Mockito.doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
TvSeries bean = (TvSeries) args[0];
//传入的值,应该和下面调用 tvSeriesService.updateTvSeries() 方法时的参数中的值相同
Assert.assertEquals(newName, bean.getName());
mockExecuted.set(0);
return 1;
}
}).when(tvSeriesDao).update(any(TvSeries.class)); TvSeries ts = new TvSeries();
ts.setName(newName);
ts.setId(111); tvSeriesService.updateTvSeries(ts);
Assert.assertTrue(mockExecuted.get(0));
}
}

测试 Controller

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc // 初始化一个 mvc 环境用于测试
public class TvSeriesControllerTest { @MockBean
TvSeriesDao tvSeriesDao;
@MockBean
TvCharacterDao tvCharacterDao; @Autowired
private MockMvc mockMvc; @Autowired
private TvSeriesController tvSeriesController; @Test
public void testGetAll() throws Exception {
List<TvSeries> list = new ArrayList<>();
TvSeries ts = new TvSeries();
ts.setName("POI");
list.add(ts); Mockito.when(tvSeriesDao.getAll()).thenReturn(list); // 相当于在启动项目后,执行 GET /tvseries,被测模块是 web 控制层,因为 web 控制层会调用业务逻辑层,所以业务逻辑层也会被测试
// 业务逻辑层调用了被 mock 出来的数据访问层桩模块。
//如果想仅仅测试 web 控制层,(例如业务逻辑层尚未编码完毕),可以 mock 一个业务逻辑层的桩模块
mockMvc.perform(MockMvcRequestBuilders.get("/tvseries"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("POI")));
} @Test
public void testAddSeries() throws Exception{
BitSet bitSet = new BitSet(1);
bitSet.set(0, false); Mockito.doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
TvSeries ts = (TvSeries) args[0];
Assert.assertEquals(ts.getName(), "疑犯追踪");
ts.setId(5432);
bitSet.set(0, true);
return 1;
}
}).when(tvSeriesDao).insert(Mockito.any(TvSeries.class)); Mockito.doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
TvCharacter tc = (TvCharacter) args[0];
// json 中传递过来的剧中角色名字
Assert.assertEquals(tc.getName(), "芬奇");
Assert.assertTrue(tc.getTvSeriesId() == 5432);
bitSet.set(0, true);
return 1;
}
}).when(tvCharacterDao).insert(Mockito.any(TvCharacter.class)); String jsonData = "{\"name\":\"疑犯追踪\",\"seasonCount\":5,\"originRelease\":\"2011-09-22\",\"tvCharacters\":[{\"name\":\"芬奇\"}]}";
this.mockMvc.perform(MockMvcRequestBuilders.post("/tvseries")
.contentType(MediaType.APPLICATION_JSON).content(jsonData))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk()); Assert.assertTrue(bitSet.get(0));
} @Test
public void testFileUpload() throws Exception{
String fileFolder = "target/files/";
File folder = new File(fileFolder);
if(!folder.exists()) {
folder.mkdirs();
}
// 设置 bean 里面通过 @Value 获得的数据
ReflectionTestUtils.setField(tvSeriesController, "uploadFolder", folder.getAbsolutePath()); InputStream is = getClass().getResourceAsStream("/testfileupload.jpg");
if(is == null) {
throw new RuntimeException("需要先在src/test/resources目录下放置一张jpg文件,名为testfileupload.jpg然后运行测试");
} //模拟一个文件上传的请求
MockMultipartFile imgFile = new MockMultipartFile("photo", "testfileupload.jpg", "image/jpeg", IOUtils.toByteArray(is)); ResultActions result = mockMvc.perform(MockMvcRequestBuilders.multipart("/tvseries/1/photos")
.file(imgFile))
.andExpect(MockMvcResultMatchers.status().isOk()); //解析返回的 JSON
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(result.andReturn().getResponse().getContentAsString(), new TypeReference<Map<String, Object>>(){}); String fileName = (String) map.get("photo");
File f2 = new File(folder, fileName);
//返回的文件名,应该已经保存在 filFolder 文件夹下
Assert.assertTrue(f2.exists());
}
}

源码:spring-boot-2-restful

Spring Boot 2.x 编写 RESTful API (五) 单元测试的更多相关文章

  1. Spring Boot 2.x 编写 RESTful API (六) 事务

    用Spring Boot编写RESTful API 学习笔记 Transactional 判定顺序 propagation isolation 脏读 不可重复读 幻读 不可重复读是指记录不同 (upd ...

  2. Spring Boot 2.x 编写 RESTful API (四) 使用 Mybatis

    用Spring Boot编写RESTful API 学习笔记 添加依赖 <dependency> <groupId>org.mybatis.spring.boot</gr ...

  3. Spring Boot 2.x 编写 RESTful API (三) 程序层次 & 数据传输

    用Spring Boot编写RESTful API 学习笔记 程序的层次结构 相邻层级的数据传输 JavaBean 有一个 public 的无参构造方法 属性 private,且可以通过 get.se ...

  4. Spring Boot 2.x 编写 RESTful API (二) 校验

    用Spring Boot编写RESTful API 学习笔记 约束规则对子类依旧有效 groups 参数 每个约束用注解都有一个 groups 参数 可接收多个 class 类型 (必须是接口) 不声 ...

  5. Spring Boot 2.x 编写 RESTful API (一) RESTful API 介绍 & RestController

    用Spring Boot编写RESTful API 学习笔记 RESTful API 介绍 REST 是 Representational State Transfer 的缩写 所有的东西都是资源,所 ...

  6. Spring Boot 集成 Swagger 生成 RESTful API 文档

    原文链接: Spring Boot 集成 Swagger 生成 RESTful API 文档 简介 Swagger 官网是这么描述它的:The Best APIs are Built with Swa ...

  7. Spring Boot 集成Swagger2生成RESTful API文档

    Swagger2可以在写代码的同时生成对应的RESTful API文档,方便开发人员参考,另外Swagger2也提供了强大的页面测试功能来调试每个RESTful API. 使用Spring Boot可 ...

  8. Spring Boot中使用Swagger2构建API文档

    程序员都很希望别人能写技术文档,自己却很不愿意写文档.因为接口数量繁多,并且充满业务细节,写文档需要花大量的时间去处理格式排版,代码修改后还需要同步修改文档,经常因为项目时间紧等原因导致文档滞后于代码 ...

  9. Spring Boot 项目学习 (四) Spring Boot整合Swagger2自动生成API文档

    0 引言 在做服务端开发的时候,难免会涉及到API 接口文档的编写,可以经历过手写API 文档的过程,就会发现,一个自动生成API文档可以提高多少的效率. 以下列举几个手写API 文档的痛点: 文档需 ...

随机推荐

  1. Windows Server 2012 R2安装SqlServer 2016

    1.系统安装 微软操作系统 Windows Server 2012 R2 官方原版镜像 Windows Server 2012 R2 是由微软公司(Microsoft)设计开发的新一代的服务器专属操作 ...

  2. CentOS 7 最小化安装配置以及网络服务器搭建、配置与管理

    一.环境介绍: 运行软件:VMware Workstation Pro 系统环境:CentOS-7-x86_64-1810 系统网络运行模式:NAT模式 二.操作配置: 1.配置动态获取IP 1) 修 ...

  3. 你的响应阻塞了没有?--Spring-WebFlux源码分析

    1. Spring WebFlux是什么? Spring WebFlux是Spring Framework 5.0中引入的新的反应式Web框架. 与Spring MVC不同,它不需要Servlet A ...

  4. 关于px,分辨率,ppi的辨析

    概述  在本篇文章的开始,我先为大家解释一下这三个名词的概念.  px全称为pixel--像素.pc及移动设备的屏幕就是通过往像素矩阵中填充颜色,从而在宏观上体现出图像.像素越小,图像越清晰.  分辨 ...

  5. SLAM+语音机器人DIY系列:(一)Linux基础——3.Linux命令行基础操作

    摘要 由于机器人SLAM.自动导航.语音交互这一系列算法都在机器人操作系统ROS中有很好的支持,所以后续的章节中都会使用ROS来组织构建代码:而ROS又是安装在Linux发行版ubuntu系统之上的, ...

  6. [Flashback]开启数据库闪回数据库功能

    Flashback是Oracle中一个重要的功能,想要使用闪回数据库功能,需要将数据库置于闪回数据库的状态. 1.检查数据库是否开启归档状态 SQL> archive log list; Dat ...

  7. java爬虫系列目录

    1. java爬虫系列第一讲-爬虫入门(爬取动作片列表) 2. java爬虫系列第二讲-爬取最新动作电影<海王>迅雷下载地址 3. java爬虫系列第三讲-获取页面中绝对路径的各种方法 4 ...

  8. vivo7.0以上系统如何无需Root激活Xposed框架的方法

    在较多公司的引流或者业务操作中,基本都需要使用安卓的黑高科技术Xposed框架,几天前我们公司购买了一批新的vivo7.0以上系统,基本都都是基于7.0以上版本,基本都不能够获取Root的su超级权限 ...

  9. 使用Linq的过程中碰到的问题

    1. 在使用linq过程DefaultIfEmpty的过程中如果 O.RS 这个支段的值是null,在取这个数据 就会报错 ,正确的写法 2. 在使用Linq 用where条件判断要好分辨大小写 3. ...

  10. Codeception 实战

    Codeception 测试 Php 代码 一.一句话概述 使用 cc 进行单元测试,保证现有代码质量,为以后维护与重构提供支撑. 二.目标 安装配置 cc 编写测试代码,简化开发与最大化稳定性和可维 ...