1. 概述

Mockito是一个用于Java单元测试的优秀强大的框架,当需要调用第三方接口而开发测试环境又无法直接调用此接口时,就可以使用Mockito模拟接口调用编写完美的单元测试,这样也使得与第三方应用进行了强解耦,更多详情请参阅Mockito官网

2. 引入Mockito依赖

由于SpringBoot自身整合了Mockito,所以在整合Mockito编写单元测试的时候,只需要引入test依赖即可

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions>
</dependency>

另外,Mockito在junit4和junit5中使用的注解有些区别,如果是使用junit4进行单元测试,还需引入junit4的依赖

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope>
</dependency>

3. 接口代码编写

本文使用SpringBoot2.3.12和MyBatis-Plus3.4.2编写Dao层和Service层接口,这里不做详细讲解,有不清楚如何使用的小伙伴请参阅SpringBoot2.3.4整合MyBatis-Plus3.4.0和Swagger3.0,此外,还会用到mapstruct,具体使用方法可参阅SpringBoot2.3整合MapStruct实现Java bean映射

4. 编写测试类

4.1. 测试类中引入mock

在测试类中引入mock有两种方法,一种是在代码中导入静态方法mock,另一种是使用注解@Mock。官网推荐使用注解方式引入mock,其优点如下:

  1. 最大限度地减少重复的模拟创建代码
  2. 使测试类更具可读性
  3. 使验证错误更易于阅读,因为字段名称用于标识模拟

注意:使用@Mock注解时,需要添加运行器使注解生效
junit4中有三种方法,具体如下:

  1. 初始化mock:MockitoAnnotations.initMocks(this)
  2. 测试类上使用注解MockitoJUnitRunner
  3. 使用MockitoRule

示例代码如下:

@Before
public void setUp() {MockitoAnnotations.initMocks(this);
}
@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class SysUserInfoServiceJunitRunnerTest {}
@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);

其中MockitoJUnitRunner有三种方式

  1. Silent:实现忽略存根参数不匹配 (MockitoJUnitRunner.StrictStubs) 并且不检测未使用的存根
  2. Strict:检测未使用的存根并将它们报告为失败
  3. StrictStubs:改进调试测试,帮助保持测试干净

MockitoRule有两种方式

  1. silent():规则不会在测试执行期间报告存根警告
  2. strictness(Strictness strictness):严格级别,尤其是“严格存根”(Strictness.STRICT_STUBS)有助于调试和保持测试清洁,另外还有严格级别LENIENT和WARN

junit5中也有三种方法,具体如下:

  1. 初始化mock:MockitoAnnotations.initMocks(this)
  2. 测试类上使用注解@ExtendWith(MockitoExtension.class)
  3. 测试类上使用注解@MockitoSettings,为测试类配置使用的严格等级

示例代码如下:

@BeforeEach
void setUp() {MockitoAnnotations.initMocks(this);
}
@ExtendWith(MockitoExtension.class)
public class SysUserInfoServiceExtensionTest {}
@MockitoSettings(strictness = Strictness.STRICT_STUBS)
public class SysUserInfoServiceSettingsTest {}

4.2. 常用注解

mock创建的注解有
@Mock:用于创建和注入模拟实例
@Spy:监视现有实例
@InjectMocks:将模拟字段自动注入到测试对象中
@Captor:用于获取参数
@Mock与@Spy的区别

  • 使用@Mock创建一个mock时,是从类型的类中创建的,而不是从实际实例中创建,mock只是创建了一个类的基本外壳实例,用于跟踪与其的交互。
  • 使用@Spy时,将包装一个现有的实例,除了行为方式与普通实例相同外,还将检测以跟踪与它的所有交互。
  • 通常情况下@Mock用于访问第三方服务接口,@Spy用于访问本服务接口(读取配置类或者mapstruct接口)

实例代码如下:

class SysUserInfoServiceSpyTest {@Mockprivate SysUserInfoMapper userInfoMapper;@Spyprivate final UserInfoMapper infoMapper = new UserInfoMapperImpl();@InjectMocksprivate SysUserInfoServiceImpl userInfoService;@BeforeEachvoid setUp() {MockitoAnnotations.initMocks(this);}
}

4.3. Stubs(存根/打桩)

使用when().thenReturn()存根有返回值的接口,使用when().thenThrow()存根有异常的接口,使用when().thenAnswer()存根有回调函数的接口,使用given().willReturn()存根BDD格式有返回值接口,第一个()中是mock对象的方法调用,第二个()中是返回的对象。
两种的功能都是一样的,given()是行为驱动开发BDD(Behavior Driven Development)的风格格式。
示例代码如下:

@Test
void getUserInfoByIdTest() {final SysUserInfo userInfo = SysUserInfo.builder().id(1L).userName("admin").password("123456").sex(2).age(99).email("admin@163.com").createUser("admin").createTime(LocalDateTime.now()).updateUser("admin").updateTime(LocalDateTime.now()).build();Mockito.when(userInfoMapper.selectById(any())).thenReturn(userInfo);// 或者// BDDMockito.given(userInfoMapper.selectById(any())).willReturn(userInfo);final SysUserInfo info = userInfoService.getById(1);Assertions.assertNotNull(info);
}

当以下情况时,使用带do的方法

  • 存根void方法
  • 在spy对象上存根方法
  • 多次存根相同的方法,以在测试过程中更改模拟的行为

常用的带do的方法有doThrow()、doAnswer()、doNothing()、doReturn()和doCallRealMethod()
doReturn():用于当监视真实对象并在spy上调用真实方法时会带来副作用,或覆盖先前的异常存根
doThrow():用于给void方法存根需要有异常抛出
doAnswer():用于给void方法存根需要有回调值
doNothing():用于给void方法存根不需要做任何事,使用情况为对void方法连续调用进行存根或者监视真实对象且void方法不执行任何操作
doCallRealMethod():用于调用真正执行的方法

4.4. 验证

verify
用于验证某些行为至少发生过一次,例如:

verify(userInfoMapper).selectById(anyInt());

或者

verify(userInfoMapper, times(1)).selectById(anyInt());

验证某些行为发生了至少一次/确切的次数/从未使用
atLeastOnce():至少发生一次

verify(userInfoMapper, atLeastOnce()).selectById(anyInt());

atLeast(num):至少发生num次

verify(userInfoMapper, atLeast(1)).selectById(anyInt());

atMostOnce():最多发生一次

verify(userInfoMapper, atMostOnce()).selectById(anyInt());

atMost(num):最多发生num次

verify(userInfoMapper, atMost(1)).selectById(anyInt());

never():从未发生过

verify(userInfoMapper, never()).selectOne(any());

only():校验的方法是否是唯一调用

verify(userInfoMapper, only()).selectById(anyInt());

timeout():给定的时间(毫秒)内一直触发验证,可以用于测试异步代码

verify(userInfoMapper, timeout(100)).selectById(anyInt());
verify(userInfoMapper, timeout(100).times(1)).selectById(anyInt());

after():给定的时间(毫秒)后触发验证,可以测试异步代码

verify(userInfoMapper, after(100)).selectById(anyInt());
verify(userInfoMapper, after(100).times(1)).selectById(anyInt());

timeout()与after()区别

  • timeout()验证通过后立即成功退出
  • after()等待给定的时间后才开始验证

ArgumentCaptor
用于获取请求参数以进行进一步断言,通常结合verify()一起使用,其适用条件如下:

  • 自定义参数匹配器不能被重用
  • 只需要对参数值进行断言即可验证

测试类中引入ArgumentCaptor可以使用注解@Captor,也可以使用

ArgumentCaptor<Class> argumentCaptor = ArgumentCaptor.forClass(Class.class);

其主要方法有capture()、getValue()、getAllValues()
capture():用于捕获参数值,必须在验证内部使用此方法

verify(userInfoMapper, times(1)).insert(argumentCaptor.capture());

getValue():返回捕获的参数值,如果验证方法被多次调用,只返回最新捕获的值

assertEquals("admin", argumentCaptor.getValue().getUserName());

getAllValues():返回捕获的所有参数值,用于捕获可变参数或多次调用验证方法,当多次调用varargs方法时,返回来自所有调用的所有值的合并列表
InOrder
用于按顺序验证mock对象,可以只验证需要的mock对象

final InOrder inOrder = inOrder(userInfoMapper, infoMapper);
inOrder.verify(userInfoMapper).selectById(anyInt());
inOrder.verify(infoMapper).map(any());

calls():允许按顺序进行非贪婪验证

inOrder.verify(userInfoMapper, calls(1)).selectById(anyInt());

与times(1)不同的是,如果该方法调用了2次也不会报错
与atLeast(1)不同的是,不会将第二次标记为已验证

SpringBoot2.3整合Mockito实现单元测试相关推荐

  1. SpringBoot2.x整合Swagger2 实现API文档实时生成

    我们提供Restful接口的时候,API文档是尤为的重要,它承载着对接口的定义,描述等,本文主要介绍了SpringBoot集成Swagger2生成接口文档的方法示例,需要的朋友们下面随着小编来一起学习 ...

  2. 视频教程-19年录制SpringBoot2.x整合微信支付在线教育网站项目实战-Java

    19年录制SpringBoot2.x整合微信支付在线教育网站项目实战 7年的开发架构经验,曾就职于国内一线互联网公司,开发工程师,现在是某创业公司技术负责人, 擅长语言有node/java/pytho ...

  3. SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题

    SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题 参考文章: (1)SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题 (2)https://www. ...

  4. mockito mock void方法_纯干货,浅谈Mockito在单元测试中的实际应用

    本文接上文"接口方没写代码,对接方只能停工吗?",在这里简单介绍Mockito在单元测试中的实际应用.本文使用场景较单一,如有雷同,不甚荣幸,闲言少叙,开门见山.本文将使用mock ...

  5. SpringBoot2.x整合Redis实战 4节课

    1.分布式缓存Redis介绍      简介:讲解为什么要用缓存和介绍什么是Redis,新手练习工具 1.redis官网 https://redis.io/download           2.新 ...

  6. SpringBoot2.x整合redis实战讲解

    SpringBoot2.x整合redis实战讲解 简介:使用springboot-starter整合reids实战 1.官网:https://docs.spring.io/spring-boot/do ...

  7. SpringBoot2.x 整合websocket 消息推送,单独发送信息,群发信息

    根据公司需求在SpringBoot项目中集成站内信,于是,我做了一个SpringBoot2.x 整合websocket 消息推送,给指定用户发送信息和群发信息即点点对方式和广播方式2种模式. 文章目录 ...

  8. SpringBoot2.0 整合 Dubbo框架 ,实现RPC服务远程调用

    一.Dubbo框架简介 1.框架依赖 图例说明: 1)图中小方块 Protocol, Cluster, Proxy, Service, Container, Registry, Monitor 代表层 ...

  9. SpringBoot2.0 整合 Redis集群 ,实现消息队列场景

    本文源码 GitHub地址:知了一笑 https://github.com/cicadasmile/middle-ware-parent 一.Redis集群简介 1.RedisCluster概念 Re ...

  10. SpringBoot2.0 整合 QuartJob ,实现定时器实时管理

    一.QuartJob简介 1.一句话描述 Quartz是一个完全由java编写的开源作业调度框架,形式简易,功能强大. 2.核心API (1).Scheduler 代表一个 Quartz 的独立运行容 ...

最新文章

  1. python工程师面试题-朋友去面试Python工程师,又带回来几道基础题,Python面试题No10...
  2. [搜索]一种改进的召回率准确率公式计算方式
  3. Windows如何上传代码到Github
  4. bitcount java_Java源码解释之Integer.bitCount
  5. 中国已与36个国家(地区)海关实现“经认证的经营者”(AEO)互认
  6. 实现Windows访问Linux文件系统
  7. phpcms加载系统类与加载应用类的区别
  8. 简单聊聊01世界中编码和解码这对磨人的小妖儿
  9. NOI Linux 2.0 桌面背景展示
  10. 桌面终端是计算机吗,桌面和终端
  11. windows/linux远程开关机原理及实现
  12. 论QQ如何发大菜狗表情
  13. 数据分析(7)路径挖掘分析法 行为序列分析法
  14. 阿里iDST NLP负责人司罗:NLP技术怎样一路走到阿里云
  15. Batch Normalization:Accelerating Deep Network Training by Reducing Internal Covariate Shift 论文笔记
  16. 认证、授权、鉴权、权限控制
  17. amber分子动力学模拟干货总结
  18. Layout(布局)
  19. 第十三周项目一(4)——验证平衡二叉树相关算法
  20. 每日一问|数据仓库面试题这些你都会吗?

热门文章

  1. 11 Django REST Framework 针对基于类的视图添加 @csrf_exempt
  2. Linux下误删除文件的各种恢复工具
  3. hosts文件导致无法网页观看视频
  4. 阿里P9手写的Java核心开发手册(2022版)覆盖P5到P8所有技术栈
  5. RHCE认证考试介绍
  6. 李开复给大学生的第6封信:选择的智慧
  7. 我的NVIDIA开发者之旅——优化显卡性能
  8. 深度优先搜索与广度优先搜索
  9. Opencv基础------RGB颜色通道的分量显示和调整
  10. 【ONNX】使用 C++ 调用 ONNX 格式的 PyTorch 深度学习模型进行预测(Windows, C++, PyTorch, ONNX, Visual Studio, OpenCV)