SpringBoot - 单元测试利器Mockito入门
文章目录
- Mock 测试
- What's Mockito
- 使用 Mockito
- pom依赖
- Demo Code
- 【常规操作】
- 【Mockito】
- thenReturn
- thenThrow
- verify
- Mockito 注意事项
Mock 测试
Mock 测试就是在测试过程中,创建一个假的对象,避免你为了测试一个方法,却要自行构建整个 Bean 的依赖链。
举个例子:
类 A 需要调用类 B 和类 C,而类 B 和类 C 又需要调用其他类如 D、E、F 等,假设类 D 是一个外部服务,那就会很难测,因为你的返回结果会直接的受外部服务影响,导致你的单元测试可能今天会过、但明天就过不了了。
而当我们引入 Mock 测试时,就可以创建一个假的对象,替换掉真实的 Bean B 和 C,这样在调用B、C的方法时,实际上就会去调用这个假的 Mock 对象的方法,而我们就可以自己设定这个 Mock 对象的参数和期望结果,让我们可以专注在测试当前的类 A,而不会受到其他的外部服务影响,这样测试效率就能提高很多。
What’s Mockito
Mockito 是一种 Java Mock 框架,主要就是用来做 Mock 测试的,它可以模拟任何 Spring 管理的 Bean、模拟方法的返回值、模拟抛出异常等等,同时也会记录调用这些模拟方法的参数、调用顺序,从而可以校验出这个 Mock 对象是否有被正确的顺序调用,以及按照期望的参数被调用。
像是 Mockito 可以在单元测试中模拟一个 Service 返回的数据,而不会真正去调用该 Service,通过模拟一个假的 Service 对象,来快速的测试当前想要测试的类。
目前在 Java 中主流的 Mock 测试工具有 Mockito、JMock、EasyMock等等,而 SpringBoot 目前默认的测试框架是 Mockito 框架。
使用 Mockito
pom依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
Demo Code
一个 UserService,两个方法
- getUserById() -------------> 调用 UserDao 这个 bean的 getUserById()
- insertUser() -------------> 调用 UserDao 这个 bean的 insertUser()
@Component
public class UserService {@Autowiredprivate UserDao userDao;public User getUserById(Integer id) {return userDao.getUserById(id);}public Integer insertUser(User user) {return userDao.insertUser(user);}
}
【POJO对象】
public class User {private Integer id;private String name;//省略 getter/setter
}
【常规操作】
如果这时候我们先不使用 Mockito 模拟一个假的 userDao Bean,而是真的去调用一个正常的 Spring Bean 的 userDao 的话, 注入 userService Bean,然后去调用方法,而他会再去调用 userDao 取得数据库的数据,然后我们再对返回结果做 Assert 断言检查。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {//先普通的注入一个userService bean@Autowiredprivate UserService userService;@Testpublic void getUserById() throws Exception {//普通的使用userService,他里面会再去调用userDao取得数据库的数据User user = userService.getUserById(1);//检查结果Assert.assertNotNull(user);Assert.assertEquals(user.getId(), new Integer(1));Assert.assertEquals(user.getName(), "John");}
}
【Mockito】
但是如果 userDao 还没写好,又想先测 userService 的话,就需要使用 Mockito 去模拟一个假的 userDao 出来。
具体怎么搞呢?
在 userDao 上加上一个 @MockBean 注解
当 userDao 被加上这个注解之后,表示 Mockito 会帮我们创建一个假的 Mock 对象,替换掉 Spring 中已存在的那个真实的 userDao Bean,也就是说,注入进 userService 的 userDao Bean,已经被我们替换成假的 Mock 对象了,所以当我们再次调用 userService 的方法时,会去调用的实际上是 mock userDao Bean 的方法,而不是真实的 userDao Bean。
当我们创建了一个假的 userDao 后,我们需要为这个 mock userDao 自定义方法的返回值,这里有一个公式用法,下面这段代码的意思为,当调用了某个 Mock 对象的方法时,就回传我们想要的自定义结果。
Mockito.when( 对象.方法名() ).thenReturn( 自定义结果 )
代码如下:
@RunWith(SpringRunner.class)
@SpringBootTest
publicclass UserServiceTest {@Autowiredprivate UserService userService;@MockBeanprivate UserDao userDao;@Testpublic void getUserById() throws Exception {// 定义当调用mock userDao的getUserById()方法,并且参数为3时,就返回id为200、name为I'm mock3的user对象Mockito.when(userDao.getUserById(3)).thenReturn(new User(200, "Aritisan"));// 返回的会是名字为I'm mock 3的user对象User user = userService.getUserById(1);Assert.assertNotNull(user);Assert.assertEquals(user.getId(), new Integer(200));Assert.assertEquals(user.getName(), "Aritisan");}
}
Mockito 除了最基本的 Mockito.when( 对象.方法名() ).thenReturn( 自定义结果 ),还提供了其他用法让我们使用。
thenReturn
当使用任何整数值调用 userService 的 getUserById() 方法时,就回传一个名字为Aritisan的 User 对象。
Mockito.when(userService.getUserById(Mockito.anyInt())).thenReturn(new User(3, "Aritisan"));
User user1 = userService.getUserById(3); // 回传的user的名字为Aritisan
User user2 = userService.getUserById(200); // 回传的user的名字也为Aritisan
限制只有当参数的数字是 3 时,才会回传名字为 Aritisan 的 user 对象。
Mockito.when(userService.getUserById(3)).thenReturn(new User(3, "Aritisan"));
User user1 = userService.getUserById(3); // 回传的user的名字为Aritisan
User user2 = userService.getUserById(200); // 回传的user为null
当调用 userService 的 insertUser() 方法时,不管传进来的 user 是什么,都回传 100。
Mockito.when(userService.insertUser(Mockito.any(User.class))).thenReturn(100);
Integer i = userService.insertUser(new User()); //会返回100
thenThrow
当调用 userService 的 getUserById() 时的参数是 9 时,抛出一个 RuntimeException。
Mockito.when(userService.getUserById(9)).thenThrow(new RuntimeException("mock throw exception"));
User user = userService.getUserById(9); //会抛出一个RuntimeException
如果方法没有返回值的话(即是方法定义为 public void myMethod() {…}),要改用 doThrow() 抛出 Exception。
Mockito.doThrow(new RuntimeException("mock throw exception")).when(userService).print();
userService.print(); //会抛出一个RuntimeException
verify
检查调用 userService 的 getUserById()、且参数为3的次数是否为1次。
Mockito.verify(userService, Mockito.times(1)).getUserById(Mockito.eq(3)) ;
验证调用顺序,验证 userService 是否先调用 getUserById() 两次,并且第一次的参数是 3、第二次的参数是 5,然后才调用insertUser() 方法。
InOrder inOrder = Mockito.inOrder(userService);
inOrder.verify(userService).getUserById(3);
inOrder.verify(userService).getUserById(5);
inOrder.verify(userService).insertUser(Mockito.any(User.class));
Mockito 注意事项
上述就是 Mockito 的 Mock 对象使用方法,不过当使用 Mockito 在 Mock 对象时,有一些限制需要遵守:
- 不能 Mock 静态方法
- 不能 Mock private 方法
- 不能 Mock final class
因此在写代码时,需要做良好的功能拆分,才能够使用 Mockito 的 Mock 技术,帮助我们降低测试时 Bean 的耦合度。
SpringBoot - 单元测试利器Mockito入门相关推荐
- mockito mock void方法_用过举手!SpringBoot 单元测试利器Mockito
Mockito 是一种 Java mock 框架,他主要是用来做 mock 测试的,他可以模拟任何 Spring 管理的 bean.模拟方法的返回值.模拟抛出异常...等,在了解 Mockito 的具 ...
- 单元测试利器——手把手教你使用Mockito
作者:京东零售 秦浩然 从你成为开发人员的那一天起,写单元测试终究是你逃不开的宿命!那开发人员为什么不喜欢写单元测试呢?究其原因,无外乎是依赖.依赖其他的服务.依赖运行的环境.等等,各种依赖都成为了我 ...
- mockito入门_Mockito入门
mockito入门 本文是我们名为" 用Mockito测试 "的学院课程的一部分. 在本课程中,您将深入了解Mockito的魔力. 您将了解有关"模拟",&qu ...
- 基于链路思想的SpringBoot单元测试快速写法
简介:本文更偏向实践而非方法论,所提及的SpringBoot单元测试写法亦并非官方解,仅仅是笔者自身觉得比较方便.效率较高的一种写法.每个团队甚至团队内的每位开发可能都有自己的写法习惯和风格,只要能实 ...
- Springboot 单元测试结合Jacoco收集单元测试覆盖率
目录 单元测试 Junit 简单例子 mockito Mock 的限制 Powermock maven 引入 注解使用 处理私有方法 mock构造函数 结合Springboot一起使用 各种runne ...
- Mockito 入门
目录 1.什么是 Mock 测试? 2.Mockito简介 3.在 SpringBoot 单元测试中使用 Mockito 3.1 Maven依赖: 3.2 UserService.java 3.3 U ...
- springboot单元测试类
springboot单元测试类,引入下方jia包 <!--version 依据当前springboot版本即可--> <dependency><groupId>or ...
- SpringBoot系列: RestTemplate 快速入门
==================================== 相关的文章 ==================================== SpringBoot系列: 与Sprin ...
- springboot环境搭建及入门必知
springboot环境搭建 导入springboot父项目依赖,目的是统一当前整合技术版本 <!--继承springboot父项目--> <parent> <g ...
最新文章
- php多级遍历,php jquery实现无限级目录遍历展示代码
- 如何移动SQL SERVER的系统数据库
- Qt 互斥锁 QMutex 的简单应用
- 前端公共reset.css模板
- 线性代数学的中国根源
- JavaScript | 声明数组并使用数组索引分配元素的代码
- Redis经典面试题
- 计算机应用基础10000字论文,计算机应用毕业设计论文一万字.docx
- jsp 页面实现增减行
- 序列每天从0开始_006 Python基础:通用序列操作
- 学好C++开发技术能从事哪些岗位?
- 浅拷贝直接赋值_Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别)?...
- P1048 采药 洛谷Oj
- 超级好用很少人知道的5款小众软件,建议收藏转发
- HTML课程导航作业,北大青年课程导航.html
- android自定义相机取景框模仿微信抓取录像实现活体认证
- fn键台式计算机在哪,fn键在哪?小鱼教您fn键使用方法
- c++编写乘法口诀表
- texture packer 处理图片空白的问题
- 【算法思想】数学归纳法在算法题中的应用(含例题举例)
热门文章
- Swift获取当前连接WIFI名称,IOS获取当前连接WIFI名称
- 学习正则表达式 - 用 HTML 标记文本
- UE4资源————Pak
- JS在VS coder界面写promt和alter语句无法在浏览器页面显示
- 7-6 分支结构——大小写字母判断 (15 分)
- 微信编辑器——构思编辑器教你如何学好图文排版
- 基于JAVA控制台版本银行自动取款机(修改版)
- 我用Python+PySide6做了个图形化番茄钟,这下可以提醒自己放松一下了。
- 天玥系列微型计算机,【简讯】AMD正式发布RX 6000系列显卡;OPPO K7x宣布…
- HTML和CSS实现京东首页(html和css详解)