1、什么是mock测试?

Mock 测试就是在测试过程中,对于某些不容易构造或者不容易获取的比较复杂的对象,用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。

2、为什么要进行Mock测试?

Mock是为了解决不同的单元之间由于耦合而难于开发、测试的问题。所以,Mock既能出现在单元测试中,也会出现在集成测试、系统测试过程中。

Mock 最大的功能是帮你 把单元测试的耦合分解开, 如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

如图,例如有这样一段程序,A接口要依赖后面的B、C,当需要测试A的时候,我们需要把整个依赖关系构造出来,比较复杂;

而使用mock的话,可以把结构进行分解,如图所示

3、Mock对象适用场景

需要将被测模块和其他依赖模块独立开,构造一个独立的测试环境,不关注被测单元的依赖对象,只关注被测单元的功能逻辑

•被测单元依赖的模块尚未开发完成,而 被测单元需要依赖模块的返回值进行后续处理

被测单元依赖的对象较难模拟或者构造比较复杂。

4、Mock测试的优势

团队可以并行工作

有了Mock,前后端人员只需要定义好接口文档就可以开始并行工作,互不影响,只在最后的联调阶段往来密切;后端与后端之间如果有接口耦合,也同样能被Mock解决;测试过程中如果遇到依赖接口没有准备好,同样可以借助Mock;

不会出现一个团队等待另一个团队的情况。这样的话,开发自测阶段就可以及早开展,从而发现缺陷的时机也提前了,有利于整个产品质量以及进度的保证

可以模拟那些无法访问的资源

比如墙

隔离系统

假如我们需要调用一个post请求,为了获得某个响应,来看当前系统是否能正确处理返回的“响应”,但是这个post请求会造成数据库中数据的污染,那么就可以充分利用Mock,构造一个虚拟的post请求,我们给他指定返回就好了。

测试覆盖度

假如有一个接口,有100个不同类型的返回,我们需要测试它在不同返回下,系统是否能够正常响应,但是有些返回在正常情况下基本不会发生

5、Mock测试存在的问题

•测试人员不应该被覆盖率高的测试所迷惑,覆盖率高不代表没有问题。需要去判断这些地方使用Mock测试是否合理,这些Mock测试是否应该换成真实模块间的调用和集成。

•当把mock接口换成实际接口后,测试/开发也必须把之前的测试重新做一遍,

建议:mock接口只能与主流程联调/ 异常返回测试,不要过分依赖mock接口进行测试。

•测试完毕,上线前请一定确保 为了mock而做的相关代码/配置文件的修改,已经完全恢复了

6、Mock测试方式

一般都是借助工具来进行mock,常见的比如fiddler。选择工具时,可以考虑一下几点

•一是数据要好管理,别让我管理一堆文件

•二是mock接口最好可以设置成和真实接口完全一致,这样就只需要切换hosts就可以切换mock接口和真实接口,不需要修改代码

•三是跨平台,mock接口在win

七、使用mock时,切记的几点:

1. 测试人员不应该被覆盖率高的自动化测试所迷惑,覆盖率高不代表没有问题。

2. 当把mock接口换成实际接口后,测试/开发也必须把之前的测试重新做一遍。

建议: mock接口只能主流程联调返回测试,不要过分依赖mock接口进行测试。

3. 测试完毕,上线前,请一定确保为Mock而做的相关代码/配置文件的修改,已经完全恢复了

建议:上线checklist中条条列出,并上线前review

八、使用Mock做单元测试

Mockito基本使用方法简介

1)、静态导入会使代码更简洁

import static org.mockito.Mockito.*;

举例:

//创建mock对象,mock一个List接口
List mockedList = mock(List.class);
//如果不使用静态导入,则必须使用Mockito调用
List mockList = Mockito.mock(List.class);

2)、验证某些行为

//你可以mock一个具体的类型,而不仅是接口
LinkedList mockedList = mock(LinkedList.class);
mockedList.add("one");
//验证
verify(mockedList).add("one");

一旦mock对象被创建了,mock对象会记住所有的交互。然后你就可能选择性地验证你感兴趣的交互。

3)、如何做一些测试桩

//测试桩
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
当调用mockList.get(0)的时候,返回first
当调用mockList.get(1)的时候,抛出一个运行时异常

4)、其他使用见上面文档

2、MockMVC基于RESTful风格的测试

对于前后端分离的项目而言,无法直接从前端静态代码中测试接口的正确性,因此可以通过MockMVC来模拟HTTP请求。基于RESTful风格的SpringMVC的测试,我们可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试。

2.1、初始化MockMvc对象

@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
//在每个测试方法执行之前都初始化MockMvc对象
@BeforeEach
public void setupMockMvc() {mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

2.2、完成一些接口的测试

1)、尝试测试一个不存在的请求 /user/1

/*** @DisplayName 自定义测试方法展示的名称* @throws Exception*/
@DisplayName("测试根据Id获取User")
@Test
void contextLoads() throws Exception {//perform,执行一个RequestBuilders请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理mockMvc.perform(MockMvcRequestBuilders//构造一个get请求.get("/user/1")//请求类型 json.contentType(MediaType.APPLICATION_JSON))// 期待返回的状态码是4XX,因为我们并没有写/user/{id}的get接口.andExpect(MockMvcResultMatchers.status().is4xxClientError());
}

展示结果:

2)、在Controller中完成 /user/{id}

/*** id:\\d+只匹配数字* @param id* @return*/
@GetMapping("/user/{id:\\d+}")
public User getUserById(@PathVariable Long id) {return userService.getById(id);
}

修改一下测试类:期待返回的结果是200

@Test
void getUserById() throws Exception {//perform,执行一个RequestBuilders请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理mockMvc.perform(MockMvcRequestBuilders//构造一个get请求.get("/user/1")//请求类型 json.contentType(MediaType.APPLICATION_JSON))// 期望的结果状态 200.andExpect(MockMvcResultMatchers.status().isOk());
}

结果展示:

3)、我们可以把结果打印到控制台

// 期望的结果状态 200
.andExpect(MockMvcResultMatchers.status().isOk())
//添加ResultHandler结果处理器,比如调试时 打印结果(print方法)到控制台
.andDo(MockMvcResultHandlers.print());

运行结果:可以看到并没有返回结果

4)、结合Mockito构建自定义返回结果

这里就用到了Mockito的应用场景,userService.getById并没有返回结果,但是我们的测试并不关心userService.getById这个方法是否正常,只是在我们的测试中需要用到这个方法,所以我们可以Mock掉UserService的getById方法,自己定义返回的结果,继续我们的测试。

@MockBean
private UserService userService;
@Test
void getUserById() throws Exception {User user = new User();user.setId(1);user.setNickname("yunqing");//Mock一个结果,当userService调用getById的时候,返回userdoReturn(user).when(userService).getById(any());//perform,执行一个RequestBuilders请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理mockMvc.perform(MockMvcRequestBuilders//构造一个get请求.get("/user/1")//请求类型 json.contentType(MediaType.APPLICATION_JSON))// 期望的结果状态 200.andExpect(MockMvcResultMatchers.status().isOk())//添加ResultHandler结果处理器,比如调试时 打印结果(print方法)到控制台.andDo(MockMvcResultHandlers.print());
}

运行结果

5)、传参数

@Test
void getUserByUsername() throws Exception {// perform : 执行请求 ;mockMvc.perform(MockMvcRequestBuilders//MockMvcRequestBuilders.get("/url") :构造一个get请求.get("/user/getUserByName")//传参.param("username","admin")// 请求type : json.contentType(MediaType.APPLICATION_JSON))// 期望的结果状态 200.andExpect(MockMvcResultMatchers.status().isOk());
}

6)、期望返回结果集有两个元素

@Test
void getAll() throws Exception {User user = new User();user.setNickname("yunqing");List<User> list = new LinkedList<>();list.add(user);list.add(user);//Mock一个结果,当userService调用list的时候,返回userwhen(userService.list()).thenReturn(list);//perform,执行一个RequestBuilders请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理mockMvc.perform(MockMvcRequestBuilders//构造一个get请求.get("/user/list")//请求类型 json.contentType(MediaType.APPLICATION_JSON))// 期望的结果状态 200.andExpect(MockMvcResultMatchers.status().isOk())//期望返回的结果集合有两个元素.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(2))//添加ResultHandler结果处理器,比如调试时 打印结果(print方法)到控制台.andDo(MockMvcResultHandlers.print());
}

运行结果:

7)、测试Post请求

@Test
void insert() throws Exception {User user = new User();user.setNickname("yunqing");String jsonResult = JSONObject.toJSONString(user);//直接自定义save返回truewhen(userService.save(any())).thenReturn(true);// perform : 执行请求 ;MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders//MockMvcRequestBuilders.post("/url") :构造一个post请求.post("/user/insert").accept(MediaType.APPLICATION_JSON)//传参,因为后端是@RequestBody所以这里直接传json字符串.content(jsonResult)// 请求type : json.contentType(MediaType.APPLICATION_JSON))// 期望的结果状态 200.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();//返回结果int statusCode = mvcResult.getResponse().getStatus();String result = mvcResult.getResponse().getContentAsString();//单个断言Assertions.assertEquals(200, statusCode);//多个断言,即使出错也会检查所有断言assertAll("断言",() -> assertEquals(200, statusCode),() -> assertTrue("true".equals(result)));

3、一些常用API总结

常用的期望:

//使用jsonPaht验证返回的json中code、message字段的返回值
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("00000"))
.andExpect(MockMvcResultMatchers.jsonPath("$.message").value("成功"))
//body属性不为空
.andExpect(MockMvcResultMatchers.jsonPath("$.body").isNotEmpty())
// 期望的返回结果集合有2个元素 , $: 返回结果
.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(2));

附带常用API解释:

RequestBuilder/MockMvcRequestBuilders:

//根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder;
MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables)
//同get类似,但是是POST方法;
MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables)
//同get类似,但是是PUT方法;
MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables)
//同get类似,但是是DELETE方法;
MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables)
//同get类似,但是是OPTIONS方法;
MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables)
//提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API;
MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables)
//提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder;
MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables)
//创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder;
RequestBuilder asyncDispatch(final MvcResult mvcResult)

MockHttpServletRequestBuilder:

//:添加头信息;
MockHttpServletRequestBuilder header(String name, Object... values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders)
//:指定请求的contentType头信息;
MockHttpServletRequestBuilder contentType(MediaType mediaType)
//:指定请求的Accept头信息;
MockHttpServletRequestBuilder accept(MediaType... mediaTypes)/MockHttpServletRequestBuilder accept(String... mediaTypes)
//:指定请求Body体内容;
MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content)
//:请求传入参数
MockHttpServletRequestBuilder param(String name,String... values)
//:指定请求的Cookie;
MockHttpServletRequestBuilder cookie(Cookie... cookies)
//:指定请求的Locale;
MockHttpServletRequestBuilder locale(Locale locale)
//:指定请求字符编码;
MockHttpServletRequestBuilder characterEncoding(String encoding)
//:设置请求属性数据;
MockHttpServletRequestBuilder requestAttr(String name, Object value)
//:设置请求session属性数据;
MockHttpServletRequestBuilder sessionAttr(String name, Object value)/MockHttpServletRequestBuilder sessionAttrs(Map<string, object=""> sessionAttributes)
//指定请求的flash信息,比如重定向后的属性信息;
MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map<string, object=""> flashAttributes)
//:指定请求的Session;
MockHttpServletRequestBuilder session(MockHttpSession session)
// :指定请求的Principal;
MockHttpServletRequestBuilder principal(Principal principal)
//:指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾;
MockHttpServletRequestBuilder contextPath(String contextPath)
//:请求的路径信息,必须以“/”开头;
MockHttpServletRequestBuilder pathInfo(String pathInfo)
//:请求是否使用安全通道;
MockHttpServletRequestBuilder secure(boolean secure)
//:请求的后处理器,用于自定义一些请求处理的扩展点;
MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor)

MockMultipartHttpServletRequestBuilder

//:指定要上传的文件;
MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder file(MockMultipartFile file)

ResultActions

//:添加验证断言来判断执行请求后的结果是否是预期的;
ResultActions andExpect(ResultMatcher matcher)
//:添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;
ResultActions andDo(ResultHandler handler)
//:返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;
MvcResult andReturn() 

ResultMatcher/MockMvcResultMatchers

//:请求的Handler验证器,比如验证处理器类型/方法名;此处的Handler其实就是处理请求的控制器;
HandlerResultMatchers handler()
//:得到RequestResultMatchers验证器;
RequestResultMatchers request()
//:得到模型验证器;
ModelResultMatchers model()
//:得到视图验证器;
ViewResultMatchers view()
//:得到Flash属性验证;
FlashAttributeResultMatchers flash()
//:得到响应状态验证器;
StatusResultMatchers status()
//:得到响应Header验证器;
HeaderResultMatchers header()
//:得到响应Cookie验证器;
CookieResultMatchers cookie()
//:得到响应内容验证器;
ContentResultMatchers content()
//:得到Json表达式验证器;
JsonPathResultMatchers jsonPath(String expression, Object ... args)/ResultMatcher jsonPath(String expression, Matcher matcher)
//:得到Xpath表达式验证器;
XpathResultMatchers xpath(String expression, Object... args)/XpathResultMatchers xpath(String expression, Map<string, string=""> namespaces, Object... args)
//:验证处理完请求后转发的url(绝对匹配);
ResultMatcher forwardedUrl(final String expectedUrl)
//:验证处理完请求后转发的url(Ant风格模式匹配,@since spring4);
ResultMatcher forwardedUrlPattern(final String urlPattern)
//:验证处理完请求后重定向的url(绝对匹配);
ResultMatcher redirectedUrl(final String expectedUrl)
//:验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4);
ResultMatcher redirectedUrlPattern(final String expectedUrl)

希望本文对你有所帮助~~如果对软件测试、接口测试、自动化测试、面试经验交流感兴趣可以私聊。免费领取最新软件测试大厂面试资料和Python自动化、接口、框架搭建学习资料!技术大牛解惑答疑,同行一起交流。

带你读懂mock测试——单元测试实践篇相关推荐

  1. 一文带你读懂Mock测试

    前言: 关于Mock测试 客户要求进行完整的产品展示,人员和时间都有限,来不及开发后端服务. 工期比较紧的项目,前端已开发完成,需要调试,后端接口还未开发完成. 公司某个项目依赖于第三方服务,但是第三 ...

  2. 一文带你读懂SDK测试

    一.什么是SDK SDK,全称:software development kit, 软件开发工具包. 软件开发工具包一般都是一些软件工程师为特定的软件包.软件框架.硬件平台.操作系统等建立应用软件时的 ...

  3. 带你读懂Spring Bean 的生命周期,嘿,就是玩儿~

    带你读懂Spring Bean 的生命周期,嘿,就是玩儿~ 一.前言 今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象.因为 Spring Bean 的 ...

  4. 一文带您读懂FCC、CE、CCC认证的区别

    一文带您读懂FCC.CE.CCC认证的区别 参考资料:https://3g.k.sohu.com/t/n411629823 FCC认证,CE认证,CCC认证是产品认证中比较常见的几个认证,前两者经常有 ...

  5. java ee 值范围_一篇文章带你读懂: Java EE

    原标题:一篇文章带你读懂: Java EE 点击上图,查看教学大纲 何为 Java EE Java EE是一个标准中间件体系结构 不要被名称"Java PlatformEnterprise ...

  6. DNN、RNN、CNN.…..一文带你读懂这些绕晕人的名词

    DNN.RNN.CNN.-..一文带你读懂这些绕晕人的名词 https://mp.weixin.qq.com/s/-A9UVk0O0oDMavywRGIKyQ 「撞脸」一直都是娱乐圈一大笑梗. 要是买 ...

  7. 2020年国家电网计算机类考纲,终于发布!详解2020届国家电网考试大纲,带你读懂考纲变化!...

    原标题:终于发布!详解2020届国家电网考试大纲,带你读懂考纲变化! 终于发布!今晚21点,详解2020届国网考试大纲,带你读懂考纲变化! 原创: 小奕 奕诚总部 今天 说来就来!"国网考试 ...

  8. 量子计算机迷宫,一个简单的例子,带你读懂量子计算机

    原标题:一个简单的例子,带你读懂量子计算机 量子计算机和你的笔记本电脑在本质上是一样的,使用的都是二进制编码.计算机语言只用0和1来表达,这被称为"位",是计算机信息量最小的单位. ...

  9. 带你读懂《深入理解计算机系统》开篇

    <深入理解计算机系统>在豆瓣评分高达9.7分,可以说是豆瓣上计算机相关书籍中评分最高的一本 一.以下是豆瓣知乎的一些评论节选给大家: 1. 还能说什么呢?能把硬件.系统.软件系统地结合起来 ...

最新文章

  1. java登录失败重新登录_为什么我的一直都是进入登录失败界面
  2. PHP的xdebug安装步骤以及遇到的坑
  3. 排序算法-07归并排序(python实现)
  4. 【插件开发】—— 13 GEF双击模型事件
  5. 顺序容器之vector
  6. mysql怎么复制信息_mysql关于复制的一些信息参考
  7. 的优先级大小_如何评估需求的优先级?
  8. 腕上“小型手机”!小米手表万事俱备 坐等发布
  9. oracle中新增一列赋值,给List里添加一字段并赋值 | 学步园
  10. JAVA的对象访问定位
  11. activiti jsp 流程设计器_「Activiti精品 悟纤出品」Activiti插件来助你一臂之力 - 第327篇...
  12. Python 字典 get() 方法
  13. java执行php代码块_Java示例讲解普通代码块以及静态代码块的执行顺序
  14. 基于ffmpeg的h264播放器无法播放HI3516开发板保存的h264码流的问题
  15. Logback文件详解
  16. Android TextView更换字体
  17. 跨境第三方支付有什么,怎么进行跨境支付?
  18. 使用DevIL来加载OpenGL纹理
  19. android华为怎么截屏快捷键,华为P9/P9Plus怎么截图 快捷键截图方法介绍
  20. CF411H 被遗忘的树 Havel定理 || 网络流

热门文章

  1. 如何修复 HTML 中的乱码
  2. Linux 安装 miniconda
  3. oracle中multiset,集合操作符Multiset
  4. neo4j中修改添加关系或者节点的属性
  5. 【HDU1698】 Just a Hook 【线段树入门】
  6. 网络编程---Ip和端口号
  7. Javascript正则入门
  8. 【机器学习报告】我用链家的数据做了一个超过链家模型的二手房房价预测模型
  9. Java粮油质量管控防伪溯源系统源码 粮油MES源码
  10. VS Code 扩展 WebTS 早期预览版发布;微软开源其搜索服务的 SPTAG 算法