Mockito 的最佳实践(mock方法)
ccwgpt 2025-07-14 15:21 3 浏览 0 评论
记得以前面试的时候,面试官问我,平常开发过程中自己会不会测试?我回答当然会呀,自己写的代码怎么不测呢。现在想想我好像误会他的意思了,他应该是想问我关于单元测试,集成测试以及背后相关的知识,然而当时说到测试,我也只知道 Junit。那么今天就说说开发过程中涉及到的测试以及相关的技术栈。
虽然测试分为单元测试,集成测试,系统测试等等,但是作为开发,我们可能不需要做这么多的测试(有时甚至不做……)接下来就说说和开发息息相关的单元测试以及集成测试。
单元测试就是模块测试,我的理解一个模块就是一个类,主要是指我们的 Service 模块,因为一个项目中大部分的业务逻辑都在 Service 层。我们专注测试 Service 中的方法即可。集成测试也就是多个模块的联合测试,Service 之间,Service 和 Dao 这种多个模块之间的综合测试。
说了测试的分类,那我们该怎么测试呢?我们常使用的就是 Junit 框架,说到测试,我之前一直没搞清楚,直到现在才有点头绪,不知道你们会不会遇到这种问题,在测试中若是涉及到 find 方法还好,但是涉及到修改数据的操作,我们的测试 case 是不能重复执行的,因为会报错,这真的很尴尬,一方面是数据不正确,一方面是数据库产生了大量了垃圾数据。
怎么避免这种情况呢?分不同的情况,若是测试的项目比较简单(表的数量不多)我们可以配置一个 test 的 profile,在文件中配置 H2 内存数据库,这样就不会影响到正常的数据库,但是 H2 相关的配置还挺烦,还要自己建表,插入相关的数据。
怎么才能在单元测试中,不需要依赖数据库,又可以保证 test case 可以重复地执行呢?这里就要说 Mock 模式了,mock 翻译过来就是 "假的",这个假指的是测试模块以外的模块都用假的实例来代替,这样既能保证测试能正常执行不影响数据库,也可以验证业务逻辑是否正确(这里我先入为主的认为单元测试主要就是测试 Service 层多样的业务逻辑)。
而 Java 中应用 Mock 模式测试最好的框架就是 Mockito,虽然也有其它 Mock 模式的测试框架,但不在我们考虑范围之内哈。我自己在项目中也使用 Mockito 框架,本来很不习惯这种什么都是 Mock 的形式,但是现在感觉,嗯,真香!所以要赶紧分享给你们。
Mockito 的优点,就是与 Junit 结合的很好,使用超方便,测试代码简洁,可读性非常好(熟悉写法之后才能感觉出来),同时也是最受欢迎的 Java 测试框架。
说一说我的感触,使用 Mockito 框架写 test case 时,一定要搞清楚什么需要假,什么需要真,不然你会发疯的,有一种都是 fake 的感觉。下面就看看怎么 quick start 吧!我这里使用的 Spring Boot2,Junit5,Mockito3,IDEA2019.3.4,Maven 的组合形式,一切都是新鲜的,最后会给出 Demo 项目,你也可以拿去练练手哦。
下面直接看个例子,这个例子我是故意这样写的,用来表示业务逻辑复杂,至少需要 3 个 case 才能百分百覆盖方法。分别是 username 不为空,username 为 "Kris",username 为空。
待测方法:
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao;
@Override
public User save(User user) {
if(!user.getUsername().isEmpty()) {
if(user.getUsername().equals("Kris")){
user.setAge(18);
return userDao.save(user);
}
return userDao.save(user);
} else {
return null;
}
}
}
标准的测试案例应该如下,这三个 test case 就可以将 save 方法百分百覆盖,我们可以使用 run with coverage 去执行测试模块,可以看到覆盖率。
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@InjectMocks
UserServiceImpl userService;
@Mock
UserDao userDao;
@Test
void save() {
Mockito.when(userDao.save(Mockito.any())).thenReturn(new User("test",12));
User user = userService.save(new User("test",12));
assertEquals("test",user.getUsername());
}
@Test
void save1() {
Mockito.when(userDao.save(Mockito.any())).thenReturn(new User("Kris",12));
User user = userService.save(new User("Kris",3));
assertEquals(12,user.getAge());
}
@Test
void save3() {
User user = userService.save(new User("",12));
assertNull(user);
}
}
下面就说说为什么这样写以及可能遇到的坑!
首先我自己就遇到一个大坑,我本地使用的是 IDEA,怎么都不能 run 测试案例,原因是在 IDEA2016.2 开始自动引入 Junit5 的 jar 包,然而这个自动引入依赖做的并不好,我的理解就是有 bug,人家 Junit5 一直在升级,需要你画蛇添足的自动引入嘛!不需要啊亲。我本地使用的 IDEA 是 2017.1 版本,Junit5 给的参考文档说最好用 2017.3 及之后的版本,搞了半天我本地还是不能 run test case, 气死人啊,不得已我升级了 IDEA,现在版本是最新的 2019.3.4 了,没问题了。
下面就是代码层面的坑了:
第一,不要忘记加 @ExtendWith(MockitoExtension.class) 这个注解 Junit5 才有,就是添加 Mock 执行的环境,初始化 Mock 实例以及严格绑定 stubbings(等下仔细介绍这个)。
第二,我们这里测试的 UserService 接口中的所有方法,注意注入待测试对象时要注入实现类 UserServiceImpl,因为我们要正经测试的是这个模块。若是有依赖其它的 Service,可以直接 @Mock 接口。
第三,待测试类中若是依赖了其它的对象,需要使用 @Mock 引入相关模块,但是待测试类本身要使用 @InjectMocks 注解,如上 UserServiceImpl。
第四,形如
Mockito.when(userDao.save(Mockito.any())).thenReturn(new User("test",12));
就是一个 stubbing(存根),我的理解是在测试过程中会遇到 userDao.save(User user) 方法,为了作假就提前设置好,类似于我把这个 stub 先存下来,反正你会遇上的,而且 Mockito3 中要求,写的 stub 必须要使用到,不然就会报
UnnecessaryStubbingException 异常,这个异常经常出现,所以说 stub 多一个不行,少一个也不行!注意看 save3 这个 caes 就没有 stub, 因为不能写,一写就报错。
第五,stub 中会定义具体的返回值,所以说,虚拟方法最终的返回值是由 thenReturn() 自定义的,和传入的参数没有关系。而且在 when() 中 Mock 的方法的参数,必须使用 Mockito.any 或类似的 Mockito.anyLong() 这种形式,不能写具体的值!既然方法都是假的,参数没有必要给真的,给了就报错,作假就要做全套嘛。
第六,最后就是测试的方法若是需要参数,一定不能参入 Mockito.any 这种虚假参数,随便你写什么,看着不要报空指针异常就行,因为你测试的就是这个方法,你还用虚拟参数,那你真的是假到家了,这里一定要想搞清楚,你到底测试的是哪个方法,你又想 Mock 哪个方法,建议和第五点一起看。
刚刚说了 stub 多了也会报错,这是因为在 Mockito3 中设置了严格的 stub,不允许你多写,但是,你可以在方法或是类上使用注解 @MockitoSettings(strictness = Strictness.LENIENT) 这样就对 stub 宽容了,多了也没关系,默认的值是 Strictness.STRICT_STUBS,我倒是建议不要改默认值,代码还简洁一点。
对了,怕你们没明白,再说一遍,Mock 是一种测试的模式,我们说 Mock 模式的测试,就是怎么在测试中做假但是又可以真正测到自己想测的模块,而 Mockito 是 Mock 模式的一种实现,是一个测试的技术框架。
在测试中,一个常见的异常就是空指针,这个比较简单发现,可能是没有 @Mock 某些依赖对象,或是没有为待测方法参数赋值,或是 thanReturn() 中定义的返回值缺少属性。
一个完美的测试案例应该是一行代码都不能少的那种,也不要多写,也不要多赋值,需要什么就添加什么。上面总结的几点都是我在写测试案例时的最佳实践,希望可以帮到你们,若是还有其它的问题,可以私信我,一起学习。
相关推荐
- RACI矩阵:项目管理中的角色与责任分配利器
-
作者:赵小燕RACI矩阵RACI矩阵是项目管理中的一种重要工具,旨在明确团队在各个任务中的角色和职责。通过将每个角色划分为负责人、最终责任人、咨询人和知情人四种类型,RACI矩阵确保每个人都清楚自己...
- 在弱矩阵组织中,如何做好项目管理工作?「慕哲制图」
-
慕哲出品必属精品系列在弱矩阵组织中,如何做好项目管理工作?【慕哲制图】-------------------------------慕哲制图系列0:一图掌握项目、项目集、项目组合、P2、商业分析和NP...
- Scrum模式:每日站会(Daily Scrum)
-
定义每日站会(DailyScrum)是一个Scrum团队在进行Sprint期间的日常会议。这个会议的主要目的是为了应对Sprint计划中的不断变化,确保团队能够有效应对挑战并达成Sprint目标。为...
- 大家都在谈论的敏捷开发&Scrum,到底是什么?
-
敏捷开发作为一种开发模式,近年来深受研发团队欢迎,与瀑布式开发相比,敏捷开发更轻量,灵活性更高,在当下多变环境下,越来越多团队选择敏捷开发。什么是敏捷?敏捷是一种在不确定和变化的环境中,通过创造和响应...
- 敏捷与Scrum是什么?(scrum敏捷开发是什么)
-
敏捷是一种思维模式和哲学,它描述了敏捷宣言中的一系列原则。另一方面,Scrum是一个框架,规定了实现这种思维方式的角色,事件,工件和规则/指南。换句话说,敏捷是思维方式,Scrum是规定实施敏捷哲学的...
- 敏捷项目管理与敏捷:Scrum流程图一览
-
敏捷开发中的Scrum流程通常可以用一个简单的流程图来表示,以便更清晰地展示Scrum框架的各个阶段和活动。以下是一个常见的Scrum流程图示例:这个流程图涵盖了Scrum框架的主要阶段和活动,其中包...
- Mockito 的最佳实践(mock方法)
-
记得以前面试的时候,面试官问我,平常开发过程中自己会不会测试?我回答当然会呀,自己写的代码怎么不测呢。现在想想我好像误会他的意思了,他应该是想问我关于单元测试,集成测试以及背后相关的知识,然而当时说到...
- EffectiveJava-5-枚举和注解(java枚举的作用与好处)
-
用enum代替int常量1.int枚举:引入枚举前,一般是声明一组具名的int常量,每个常量代表一个类型成员,这种方法叫做int枚举模式。int枚举模式是类型不安全的,例如下面两组常量:性别和动物种...
- Maven 干货 全篇共:28232 字。预计阅读时间:110 分钟。建议收藏!
-
Maven简介Maven这个词可以翻译为“知识的积累”,也可以翻译为“专家”或“内行”。Maven是一个跨平台的项目管理工具。主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。仔...
- Java单元测试框架PowerMock学习(java单元测试是什么意思)
-
前言高德的技术大佬在谈论方法论时说到:“复杂的问题要简单化,简单的问题要深入化。”这句话让我感触颇深,这何尝不是一套编写代码的方法——把一个复杂逻辑拆分为许多简单逻辑,然后把每一个简单逻辑进行深入实现...
- Spring框架基础知识-第六节内容(Spring高级话题)
-
Spring高级话题SpringAware基本概念Spring的依赖注入的最大亮点是你所有的Bean对Spring容器的存在是没有意识的。但是在实际的项目中,你的Bean必须要意识到Spring容器...
- Java单元测试浅析(JUnit+Mockito)
-
作者:京东物流秦彪1.什么是单元测试(1)单元测试环节:测试过程按照阶段划分分为:单元测试、集成测试、系统测试、验收测试等。相关含义如下:1)单元测试:针对计算机程序模块进行输出正确性检验工作...
- 揭秘Java代码背后的质检双侠:JUnit与Mockito!
-
你有没有发现,现在我们用的手机App、逛的网站,甚至各种智能设备,功能越来越复杂,但用起来却越来越顺畅,很少遇到那种崩溃、卡顿的闹心事儿?这背后可不是程序员一拍脑袋写完代码就完事儿了!他们需要一套严谨...
- 单元测试框架哪家强?Junit来帮忙!
-
大家好,在前面的文章中,给大家介绍了以注解和XML的方式分别实现IOC和依赖注入。并且我们定义了一个测试类,通过测试类来获取到了容器中的Bean,具体的测试类定义如下:@Testpublicvoid...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- JAVA集合框架 (47)
- mfc框架 (52)
- abb框架断路器 (48)
- ui自动化框架 (47)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (65)
- ppt框架 (48)
- 内联框架 (52)
- cad怎么画框架 (58)
- ps怎么画框架 (47)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)
- oracle提交事务 (47)