2.5 使用注解进行单元测试详解
Mockito 使用注解进行单元测试详解
Mockito 提供了一系列注解来简化测试代码的编写,减少手动创建和管理 Mock 对象的样板代码。结合 JUnit 5,可以更高效地构建清晰、易维护的单元测试。
1. 核心注解概览
| 注解 | 作用 |
|---|---|
@Mock | 创建并注入一个 Mock 对象(完全模拟,方法默认返回空或默认值)。 |
@Spy | 创建并注入一个 Spy 对象(部分模拟,默认调用真实方法,除非显式覆盖)。 |
@InjectMocks | 自动将 @Mock 或 @Spy 对象注入到被测类中(依赖注入)。 |
@Captor | 自动初始化 ArgumentCaptor,用于捕获方法参数。 |
@ExtendWith | 启用 Mockito 扩展(JUnit 5 必需),替代旧版 @RunWith。 |
2. 注解配置与启用
2.1 启用 Mockito 支持
在测试类上添加 @ExtendWith(MockitoExtension.class),激活 Mockito 注解功能:
@ExtendWith(MockitoExtension.class) // JUnit 5 必加
public class UserServiceTest {// 测试代码...
}
2.2 自动初始化注解
无需手动调用 MockitoAnnotations.openMocks(this),@ExtendWith 已自动处理。
3. 注解使用详解
3.1 @Mock 注解
作用:创建完全模拟的依赖对象。
示例场景:
public class UserService {private final UserDao userDao;public UserService(UserDao userDao) {this.userDao = userDao;}public User getUserById(int id) {return userDao.findById(id);}
}
测试代码:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {@Mockprivate UserDao mockUserDao; // 自动创建 Mock 对象@InjectMocksprivate UserService userService; // 自动注入 mockUserDao@Testvoid getUserById_ShouldReturnUser() {// 配置 Mock 行为when(mockUserDao.findById(1)).thenReturn(new User(1, "Alice"));// 调用被测方法User user = userService.getUserById(1);// 验证结果assertEquals("Alice", user.getName());verify(mockUserDao).findById(1);}
}
3.2 @Spy 注解
作用:创建部分模拟对象,保留真实方法逻辑,除非显式覆盖。
示例场景:
public class PaymentService {public boolean validateCard(String cardNumber) {return cardNumber != null && cardNumber.length() == 16;}public boolean processPayment(String cardNumber) {if (!validateCard(cardNumber)) return false;// 真实支付逻辑...return true;}
}
测试代码:
@ExtendWith(MockitoExtension.class)
class PaymentServiceTest {@Spy // 部分模拟,保留真实方法private PaymentService spyPaymentService;@Testvoid processPayment_ShouldUseMockedValidation() {// 覆盖 validateCard 方法doReturn(true).when(spyPaymentService).validateCard(anyString());// 调用被测方法(processPayment 会调用被覆盖的 validateCard)boolean result = spyPaymentService.processPayment("invalid_card");assertTrue(result);verify(spyPaymentService).validateCard("invalid_card");}
}
3.3 @InjectMocks 注解
作用:自动将 @Mock 或 @Spy 对象注入到被测类中。
注入规则:
- 构造器注入(优先):匹配参数类型和数量。
- Setter 注入:调用 setter 方法。
- 字段注入(最后):直接反射注入字段。
示例:
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {@Mockprivate InventoryService inventoryService;@Mockprivate PaymentService paymentService;@InjectMocks // 自动注入 inventoryService 和 paymentServiceprivate OrderService orderService;@Testvoid placeOrder_ShouldCheckInventory() {when(inventoryService.checkStock(anyString())).thenReturn(true);orderService.placeOrder("product_123");verify(inventoryService).checkStock("product_123");}
}
3.4 @Captor 注解
作用:自动创建参数捕获器,简化参数验证。
示例:
@ExtendWith(MockitoExtension.class)
class NotificationServiceTest {@Mockprivate EmailClient mockEmailClient;@InjectMocksprivate NotificationService notificationService;@Captor // 自动初始化 ArgumentCaptorprivate ArgumentCaptor<EmailRequest> emailCaptor;@Testvoid sendWelcomeEmail_ShouldCaptureEmailContent() {notificationService.sendWelcomeEmail("user@example.com");verify(mockEmailClient).send(emailCaptor.capture());EmailRequest captured = emailCaptor.getValue();assertEquals("user@example.com", captured.getTo());assertTrue(captured.getSubject().contains("Welcome"));}
}
4. 常见问题与解决方案
| 问题 | 解决方案 |
|---|---|
@Mock 对象为 null | 检查是否添加 @ExtendWith(MockitoExtension.class)。 |
| 依赖注入失败 | 确保 @InjectMocks 类的依赖项有对应的 @Mock 或 @Spy 对象。 |
| Spy 对象调用真实方法导致异常 | 使用 doReturn().when() 替代 when().thenReturn() 避免执行真实方法。 |
| 参数捕获器未初始化 | 使用 @Captor 替代手动创建 ArgumentCaptor。 |
5. 高级整合:与 Spring Boot 测试结合
在 Spring Boot 测试中,可使用 @MockBean 替换容器中的 Bean:
@SpringBootTest
public class ProductServiceIntegrationTest {@MockBean // Spring 管理的 Mockprivate InventoryService mockInventoryService;@Autowiredprivate ProductService productService;@Testvoid reserveProduct_ShouldUseMockInventory() {when(mockInventoryService.reserve(anyString())).thenReturn(true);boolean result = productService.reserveProduct("product_123");assertTrue(result);}
}
6. 最佳实践
- 保持测试简洁:使用注解减少手动初始化代码。
- 明确依赖关系:通过
@InjectMocks明确被测类的依赖注入方式。 - 避免过度 Mock:仅 Mock 外部依赖,保留核心逻辑的真实性。
- 结合 AssertJ:使用流式断言提高测试可读性:
assertThat(capturedEmail.getSubject()).contains("Welcome");
通过合理使用 Mockito 注解,可以显著提升单元测试的编写效率和可维护性。
相关文章:
2.5 使用注解进行单元测试详解
Mockito 使用注解进行单元测试详解 Mockito 提供了一系列注解来简化测试代码的编写,减少手动创建和管理 Mock 对象的样板代码。结合 JUnit 5,可以更高效地构建清晰、易维护的单元测试。 1. 核心注解概览 注解作用Mock创建并注入一个 Mock 对象…...
当没有OpenGL时,Skia如何绘制?
Skia 是可以在没有 OpenGL 的情况下进行图形绘制的,但是具体能否成功绘制图形,取决于 Skia 是如何配置的,以及平台上是否提供了其他的底层图形 API。 Skia 的底层依赖 Skia 的目标是提供一种跨平台的 2D 图形绘制接口。为了加速图形渲染&…...
SaaS+AI应用架构:业务场景、智能体、大模型、知识库、传统工具系统
SaaSAI应用架构:业务场景、智能体、大模型、知识库、传统工具系统 大家好,我是汤师爷~ 在SaaS与AI应用的演进过程中,合理的架构设计至关重要。本节将详细介绍其五个核心层次: 业务场景层:发现和确定业务场景智能体层…...
Go 语言中如何高效地处理集合
文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons:JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram,自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 ? 5 IDEA必装的插件&…...
布隆过滤器到底是什么东西?它有什么用
布隆过滤器:用概率换空间的奇妙数据结构 引言:当空间成为奢侈品 在互联网每天产生2.5万亿字节数据的时代,Google每秒处理超过9万次搜索请求,Redis缓存系统支撑着百万级QPS的访问。面对如此海量的数据处理需求,传统的…...
【数据结构初阶第十节】队列(详解+附源码)
好久不见。。。别不开心了,听听喜欢的歌吧 必须有为成功付出代价的决心,然后想办法付出这个代价。云边有个稻草人-CSDN博客 目录 一、概念和结构 二、队列的实现 Queue.h Queue.c test.c Relaxing Time! ————————————《有没…...
沪深300股指期权能对股指期货进行完全套保吗?
锦鲤三三每日分享期权知识,帮助期权新手及时有效地掌握即市趋势与新资讯! 沪深300股指期权能对股指期货进行完全套保吗? 沪深300股指期权是以沪深300指数为标的物的期权,而沪深300股指期货则是以该指数作为标的的期货合约。 理…...
JAVA学习第三天
继承关系变量访问的特点 01.方法中找 02.子类变量定义中找 03.父类中找 this和super关键字的使用区别: super父类构造函数的使用: 使用子类构造函数时,都会初始化父类的数据,自动调用父类的无参构造函数 super内存图——007 继…...
win11电脑其他WiFi可以连,只有一个WiFi连不上
这个问题卡了一小会,查了一些资料 后面发现 点击“诊断网络问题” 显示没有响应 第一步 重启wlan网络适配器 解决!!! 重新连接那个有问题的wifi,丝滑连接!...
leetcode_1760 袋子里最少数目的球
1. 题意 给定一个数组,和一个最多次操作次数。每次操作可以将数组中的一个数 x x x分成两个数 t x − t t\quad x-t tx−t。问 m a x O p e r a t i o n C n t maxOperationCnt maxOperationCnt次操作后,数组中最大的数最小的值是多少。 2. 题解 这个…...
Python 面向对象的三大特征
前言:本篇讲解面向对象的三大特征(封装,继承,多态),还有比较细致的(类属性类方法,静态方法),分步骤讲解,比较适合理清楚三大特征的思路 面向对象的…...
Linux下的进程切换与调度
目录 1.进程的优先级 优先级是什么 Linux下优先级的具体做法 优先级的调整为什么要受限 2.Linux下的进程切换 3.Linux下进程的调度 1.进程的优先级 我们在使用计算机的时候,通常会启动多个程序,这些程序最后都会变成进程,但是我们的硬…...
面向对象程序设计-实验六
7-1 函数重载(数据类型不同) 代码清单: #include<iostream> using namespace std; class axxx { public: void px(int n,int a[]) { for(int i0;i<n;i) { for(int j0;j<n-i-1;j) { int t; if(a[j]>a[j1]) { ta[j]; a[j…...
MongoDB 7 分片副本集升级方案详解(上)
#作者:任少近 文章目录 前言:Mongodb版本升级升级步骤环境1.1环境准备1.2standalone升级1.3分片、副本集升级 前言:Mongodb版本升级 在开始升级之前,请参阅 MongoDB下个版本中的兼容性变更文档,以确保您的应用程序和…...
【工业安全】-CVE-2022-35555- Tenda W6路由器 命令注入漏洞
文章目录 1.漏洞描述 2.环境搭建 3.漏洞复现 4.漏洞分析 4.1:代码分析 4.2:流量分析 5.poc代码: 1.漏洞描述 漏洞编号:CVE-2022-35555 漏洞名称:Tenda W6 命令注入 威胁等级:高危 漏洞详情࿱…...
算法分析 ——《模拟》
文章目录 《替换所有的问号》题目描述:代码演示:代码解析: 《提莫攻击》题目描述:代码演示:代码解析: [《Z 字形变换》](https://leetcode.cn/problems/zigzag-conversion/)题目描述:代码演示&a…...
将Sqlite3数据库挂在内存上处理
创作灵感:最近把小学生的口算题从2位数改到3位数,100以内四则运算练习(千纬数学)再次更新,选取难题-CSDN博客要不断刷题目,以前100以内的加减乘除也是这样刷出来的,代码如下: impor…...
前端大屏适配方案:从设计到实现的全流程指南
引言 随着数据可视化需求的增长,大屏展示项目在前端开发中越来越常见。然而,大屏开发面临独特的挑战: 屏幕分辨率多样:从1080P到4K甚至8K,如何保证清晰度?布局复杂:多图表、多组件如何合理排列…...
学习总结三十二
map #include<iostream> #include<map> using namespace std;int main() {//首先创建一个map对象map<int, char>oneMap;//插入数据oneMap.insert(pair<int, char>(1, A));oneMap.insert(make_pair(2,B));oneMap.insert(map<int,char>::value_ty…...
飞书专栏-TEE文档
CSDN学院课程连接:https://edu.csdn.net/course/detail/39573...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
