用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. vue项目首屏加载优化实战

    问题 单页面应用的一个问题就是首页加载东西过多,加载时间过长.特别在移动端,单页面应用的首屏加载优化更是绕不开的话题.下面我会写出我在项目中做的一些优化,希望大家能够相互讨论,共同进步. 我的项目vu ...

  2. 图像检索(3):BoW实现

    在上一篇文章中图像检索(2):均值聚类-构建BoF中,简略的介绍了基于sift特征点的BoW模型的构建,以及基于轻量级开源库vlfeat的一个简单实现. 本文重新梳理了一下BoW模型,并给出不同的实现 ...

  3. Spring基础系列-Web开发

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9996902.html SpringBoot基础系列-web开发 概述 web开发就是集成 ...

  4. 【转载】java 中变量的存储位置

    原文链接点这里,感谢博主分享 * 寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. * 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出 ...

  5. [Java] 为什么字符串比较不能用两个等号(==)

    Java中,使用"=="比较字符串时,判断的是两个字符串是否存放在相同的位置.如果两个字符串存放在相同的位置,那么它们就是相同的,使用"=="比较的结果也就是T ...

  6. jenkins实现以gitlab为代码仓库的构建

    简介 前一篇随笔是安装jenkins的过程,比较简单,这一次说一下用jenkins配置以gitlab为代码管理仓库的maven项目的完整个构建过程,以及我碰到的一些问题.由于是maven项目,所以我们 ...

  7. 25 ,CSS 构造表格

    1. 表格的基础构造 2. 边距和边线应用 3. 隐藏和删除应用 1.  简单表格 table { width:auto; border-collapse:collapse; margin-left: ...

  8. springmvc流程图

  9. linux 子系统折腾记 (三)

    所以说,英文真是个好东西,很多资料都只有英文版本,要是不懂英文,甚至你不知道这个资料的存在,更别提用蹩脚的翻译软件去翻译了. wsl 的资料:https://docs.microsoft.com/zh ...

  10. Scrum笔记

    Scrum的笔记,需要的童鞋拿去,有错漏处请指正,谢谢. 出处:https://www.cnblogs.com/Ryu666/p/9890609.html