SpringBoot2.3整合Mockito实现单元测试
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,其优点如下:
- 最大限度地减少重复的模拟创建代码
- 使测试类更具可读性
- 使验证错误更易于阅读,因为字段名称用于标识模拟
注意:使用@Mock注解时,需要添加运行器使注解生效
junit4中有三种方法,具体如下:
- 初始化mock:MockitoAnnotations.initMocks(this)
- 测试类上使用注解MockitoJUnitRunner
- 使用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有三种方式
- Silent:实现忽略存根参数不匹配 (MockitoJUnitRunner.StrictStubs) 并且不检测未使用的存根
- Strict:检测未使用的存根并将它们报告为失败
- StrictStubs:改进调试测试,帮助保持测试干净
MockitoRule有两种方式
- silent():规则不会在测试执行期间报告存根警告
- strictness(Strictness strictness):严格级别,尤其是“严格存根”(Strictness.STRICT_STUBS)有助于调试和保持测试清洁,另外还有严格级别LENIENT和WARN
junit5中也有三种方法,具体如下:
- 初始化mock:MockitoAnnotations.initMocks(this)
- 测试类上使用注解@ExtendWith(MockitoExtension.class)
- 测试类上使用注解@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实现单元测试相关推荐
- SpringBoot2.x整合Swagger2 实现API文档实时生成
我们提供Restful接口的时候,API文档是尤为的重要,它承载着对接口的定义,描述等,本文主要介绍了SpringBoot集成Swagger2生成接口文档的方法示例,需要的朋友们下面随着小编来一起学习 ...
- 视频教程-19年录制SpringBoot2.x整合微信支付在线教育网站项目实战-Java
19年录制SpringBoot2.x整合微信支付在线教育网站项目实战 7年的开发架构经验,曾就职于国内一线互联网公司,开发工程师,现在是某创业公司技术负责人, 擅长语言有node/java/pytho ...
- SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题
SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题 参考文章: (1)SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题 (2)https://www. ...
- mockito mock void方法_纯干货,浅谈Mockito在单元测试中的实际应用
本文接上文"接口方没写代码,对接方只能停工吗?",在这里简单介绍Mockito在单元测试中的实际应用.本文使用场景较单一,如有雷同,不甚荣幸,闲言少叙,开门见山.本文将使用mock ...
- SpringBoot2.x整合Redis实战 4节课
1.分布式缓存Redis介绍 简介:讲解为什么要用缓存和介绍什么是Redis,新手练习工具 1.redis官网 https://redis.io/download 2.新 ...
- SpringBoot2.x整合redis实战讲解
SpringBoot2.x整合redis实战讲解 简介:使用springboot-starter整合reids实战 1.官网:https://docs.spring.io/spring-boot/do ...
- SpringBoot2.x 整合websocket 消息推送,单独发送信息,群发信息
根据公司需求在SpringBoot项目中集成站内信,于是,我做了一个SpringBoot2.x 整合websocket 消息推送,给指定用户发送信息和群发信息即点点对方式和广播方式2种模式. 文章目录 ...
- SpringBoot2.0 整合 Dubbo框架 ,实现RPC服务远程调用
一.Dubbo框架简介 1.框架依赖 图例说明: 1)图中小方块 Protocol, Cluster, Proxy, Service, Container, Registry, Monitor 代表层 ...
- SpringBoot2.0 整合 Redis集群 ,实现消息队列场景
本文源码 GitHub地址:知了一笑 https://github.com/cicadasmile/middle-ware-parent 一.Redis集群简介 1.RedisCluster概念 Re ...
- SpringBoot2.0 整合 QuartJob ,实现定时器实时管理
一.QuartJob简介 1.一句话描述 Quartz是一个完全由java编写的开源作业调度框架,形式简易,功能强大. 2.核心API (1).Scheduler 代表一个 Quartz 的独立运行容 ...
最新文章
- python工程师面试题-朋友去面试Python工程师,又带回来几道基础题,Python面试题No10...
- [搜索]一种改进的召回率准确率公式计算方式
- Windows如何上传代码到Github
- bitcount java_Java源码解释之Integer.bitCount
- 中国已与36个国家(地区)海关实现“经认证的经营者”(AEO)互认
- 实现Windows访问Linux文件系统
- phpcms加载系统类与加载应用类的区别
- 简单聊聊01世界中编码和解码这对磨人的小妖儿
- NOI Linux 2.0 桌面背景展示
- 桌面终端是计算机吗,桌面和终端
- windows/linux远程开关机原理及实现
- 论QQ如何发大菜狗表情
- 数据分析(7)路径挖掘分析法 行为序列分析法
- 阿里iDST NLP负责人司罗:NLP技术怎样一路走到阿里云
- Batch Normalization:Accelerating Deep Network Training by Reducing Internal Covariate Shift 论文笔记
- 认证、授权、鉴权、权限控制
- amber分子动力学模拟干货总结
- Layout(布局)
- 第十三周项目一(4)——验证平衡二叉树相关算法
- 每日一问|数据仓库面试题这些你都会吗?
热门文章
- 11 Django REST Framework 针对基于类的视图添加 @csrf_exempt
- Linux下误删除文件的各种恢复工具
- hosts文件导致无法网页观看视频
- 阿里P9手写的Java核心开发手册(2022版)覆盖P5到P8所有技术栈
- RHCE认证考试介绍
- 李开复给大学生的第6封信:选择的智慧
- 我的NVIDIA开发者之旅——优化显卡性能
- 深度优先搜索与广度优先搜索
- Opencv基础------RGB颜色通道的分量显示和调整
- 【ONNX】使用 C++ 调用 ONNX 格式的 PyTorch 深度学习模型进行预测(Windows, C++, PyTorch, ONNX, Visual Studio, OpenCV)