当前位置: 首页 > news >正文

QA之二 - 单元测试--Mockito

一、是什么
Mockito 是 Java 生态最主流的模拟框架(Mock Framework),能创建 “模拟对象(Mock Object)” 替代真实的外部依赖(如 RPC 客户端、数据库连接、第三方 API),让单元测试完全隔离外部系统,只关注被测代码的逻辑。

核心概念:

  • Mock 对象:模拟外部依赖的 “假对象”,可定制返回值 / 验证交互,比如模拟 RpcClient 类,替代真实的链上 RPC 连接;
  • Stub(存根):仅定制 Mock 对象的返回值(给数据),如让 Mock 的 getBlockNumber() 固定返回 123456;
  • 行为验证:验证被测代码是否调用了 Mock 对象的指定方法,比如验证多签服务是否调用了 RpcClient.sendTx() 方法;

二、怎么用
1、引入Maven依赖,配合JUnit5

点击查看代码
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>Junit5</artifactId><version>1.0-SNAPSHOT</version><properties><!-- 统一指定项目编码为 UTF-8 --><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!-- 统一指定项目报告编码(可选,避免报告乱码) --><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><maven.compiler.source>21</maven.compiler.source><maven.compiler.target>21</maven.compiler.target></properties><dependencies><!-- JUnit 5 核心依赖 --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.9.2</version><scope>test</scope></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-engine</artifactId><version>5.9.2</version><scope>test</scope></dependency><!-- 参数化测试依赖 --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-params</artifactId><version>5.9.2</version><scope>test</scope></dependency><!-- Mockito 核心依赖 --><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>4.11.0</version><scope>test</scope></dependency><!-- Mockito 适配 JUnit 5 的扩展 --><dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><version>4.11.0</version><scope>test</scope></dependency><!-- 可选:静态方法Mock依赖(如需Mock静态工具类) --><dependency><groupId>org.mockito</groupId><artifactId>mockito-inline</artifactId><version>4.11.0</version><scope>test</scope></dependency></dependencies><build><!-- 执行快速、独立的单元测试(失败会直接中断构建)--><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.1.2</version></plugin><!-- 专门用于执行集成测试(Integration Tests) --><!--<plugin><artifactId>maven-failsafe-plugin</artifactId><version>3.1.2</version></plugin>--></plugins></build>
</project>

2、Mockito 基础用法
Mockito 的核心操作分为 3 步:创建 Mock 对象 → 定制 Mock 行为(Stub) → 执行测试 & 验证交互,以下是基础用法:
2.1. 核心注解(简化 Mock 对象创建)
Mockito 提供注解替代手动创建 Mock 对象,结合 JUnit 5 需在测试类上加 @ExtendWith(MockitoExtension.class):

  • @Mock:创建 Mock 对象;
  • @InjectMocks:自动将 Mock 对象注入到被测对象中(依赖注入);
  • @Spy:包装真实对象(部分模拟:真实方法 + Mock 方法);
  • @Captor:捕获方法调用的参数(验证参数是否正确)
基础示例(注解使用)
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;// 核心注解:启用 Mockito 对 JUnit 5 的支持
@ExtendWith(MockitoExtension.class)
class MultisigServiceTest {// 1. 创建 Mock 对象(模拟链上RPC客户端)@Mockprivate RpcClient rpcClient;// 2. 将 Mock 对象注入到被测对象中(MultisigService 依赖 RpcClient)@InjectMocksprivate MultisigService multisigService; // 被测对象// 测试方法...
}

2.2. 定制 Mock 行为(Stub:给 Mock 对象 “设定返回值”)
用 when(...).thenReturn(...) 定制 Mock 方法的返回值,或 when(...).thenThrow(...) 模拟异常场景:
(1)基础返回值定制

点击查看代码
import static org.mockito.Mockito.*;@Test
@DisplayName("模拟RPC客户端返回区块高度")
void testMockRpcClient() {// 定制行为:当调用 rpcClient.getBlockNumber() 时,返回 123456when(rpcClient.getBlockNumber()).thenReturn(123456L);// 调用被测方法(内部会调用 rpcClient.getBlockNumber())long blockNumber = multisigService.getCurrentBlockNumber();// 断言结果(验证被测方法逻辑)assertEquals(123456L, blockNumber);
}

(2)模拟异常场景(如 RPC 调用失败)

点击查看代码
@Test
@DisplayName("模拟RPC调用失败抛出异常")
void testRpcClientException() {// 定制行为:调用 rpcClient.sendTx("0x123") 时抛出 RuntimeExceptionwhen(rpcClient.sendTx("0x123")).thenThrow(new RuntimeException("RPC连接超时"));// 断言被测方法会捕获异常并返回falseboolean result = multisigService.executeTransaction("0x123");assertFalse(result);
}

(3)多次调用返回不同值(链式调用)

点击查看代码
@Test
@DisplayName("模拟多次调用RPC返回不同值")
void testMultipleCalls() {// 第一次调用返回 123456,第二次返回 123457when(rpcClient.getBlockNumber()).thenReturn(123456L).thenReturn(123457L);// 验证多次调用结果assertEquals(123456L, rpcClient.getBlockNumber());assertEquals(123457L, rpcClient.getBlockNumber());
}

(4)参数匹配(模糊匹配参数)
当不知道方法调用的具体参数,或需要匹配一类参数时,用 Mockito 的参数匹配器:

  • any():匹配任意类型参数:when(rpcClient.sendTx(any())).thenReturn(true)
  • anyString():匹配任意字符串:when(rpcClient.getTx(anyString())).thenReturn(tx)
  • eq("0x123"):匹配指定值:when(rpcClient.sendTx(eq("0x123"))).thenReturn(true)
  • isNull()/notNull():匹配空 / 非空参数:when(rpcClient.getTx(isNull())).thenThrow(IllegalArgumentException.class)
点击查看代码
@Test
@DisplayName("参数匹配器:模拟任意交易ID返回成功")
void testArgumentMatcher() {// 定制行为:sendTx 接收任意字符串参数时,都返回 truewhen(rpcClient.sendTx(anyString())).thenReturn(true);// 验证不同参数都返回 trueassertTrue(rpcClient.sendTx("0x123"));assertTrue(rpcClient.sendTx("0x456"));
}

2.3. 行为验证(验证 Mock 对象是否被正确调用)
除了验证返回值,还能验证 “被测代码是否调用了 Mock 对象的指定方法”“调用次数是否正确”:

核心验证语法
// 基础验证:验证方法被调用过一次
verify(rpcClient).sendTx("0x123");// 验证调用次数
verify(rpcClient, times(2)).sendTx(anyString()); // 调用2次
verify(rpcClient, never()).sendTx("0x789"); // 从未调用
verify(rpcClient, atLeastOnce()).getBlockNumber(); // 至少调用1次
verify(rpcClient, atMost(3)).getBlockNumber(); // 最多调用3次// 验证方法未被调用
verifyNoInteractions(rpcClient); // 完全未调用
verifyNoMoreInteractions(rpcClient); // 除了已验证的调用,无其他调用

2.4. @Spy 部分模拟(真实对象 + Mock 方法)
@Spy 用于包装真实对象,既可以调用真实方法,也可以 Mock 部分方法(适合 “大部分逻辑用真实,少数方法模拟” 的场景):

点击查看代码
@Test
@DisplayName("@Spy 部分模拟时间锁服务")
void testSpy() {// 创建真实对象的 Spy 包装TimelockService timelockService = new TimelockService();TimelockService spyService = spy(timelockService);// 真实方法:调用 calculateDelay() 执行真实逻辑assertEquals(3600, spyService.calculateDelay());// Mock 方法:定制 isTimeLockPassed() 返回 falsedoReturn(false).when(spyService).isTimeLockPassed("0x123");assertFalse(spyService.isTimeLockPassed("0x123"));
}

关键注意

  • @Spy 和 @Mock 的区别:@Mock 是完全模拟(所有方法默认返回默认值),@Spy 是部分模拟(默认调用真实方法);
  • 定制 @Spy 方法的返回值,需用 doReturn(...) 而非 when(...)(避免触发真实方法)。

2.5. @Captor 参数捕获(验证方法调用的参数)
用 @Captor 捕获 Mock 方法调用时传入的参数,验证参数是否符合预期(如 “多签服务调用 RPC 时,传入的交易 ID 是否正确”):

点击查看代码
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;@ExtendWith(MockitoExtension.class)
class MultisigServiceTest {@Mockprivate RpcClient rpcClient;@InjectMocksprivate MultisigService multisigService;// 定义参数捕获器(捕获 String 类型的参数)@Captorprivate ArgumentCaptor<String> txIdCaptor;@Test@DisplayName("捕获参数:验证调用RPC的交易ID是否正确")void testArgumentCaptor() {// 1. 执行被测方法(内部会调用 rpcClient.sendTx("0x123456"))multisigService.executeTransaction("0x123456");// 2. 捕获 sendTx 方法的参数verify(rpcClient).sendTx(txIdCaptor.capture());// 3. 验证捕获的参数String capturedTxId = txIdCaptor.getValue();assertEquals("0x123456", capturedTxId);}
}
http://www.jsqmd.com/news/412210/

相关文章:

  • 【大数据毕设全套源码+文档】基于django+大数据技术的网络招聘信息的数据挖掘研究的设计与实现(丰富项目+远程调试+讲解+定制)
  • 天崩开局,AI 又又又犯错了
  • 为什么不能直接在项目 DTS 中引用平台的dts
  • 程序员如何利用AI进行用户画像分析
  • 2026年算法备案实战全解析:从“双审”逻辑到材料避坑,后端架构视角下的合规指南
  • 上海净水器批发:核心知识点+靠谱供应商推荐 - 小坤哥
  • 2026 想读 MBA?TOP4国内优质项目优选指南来了! - 速递信息
  • rk3588 android12 屏幕翻转 触摸翻转
  • 《计算机视觉:从入门到精通》技术手册 第25章 可解释AI与视觉推理
  • Android 13 RK3588 编译烧写实录全程
  • Jam创建项目工程源码分析(1) 解析命令行参数
  • 《计算机视觉:从入门到精通》技术手册 第23章 自动驾驶视觉系统
  • 不聊房子、不卷票子,「全民健康热」带火了阿福
  • 《计算机视觉:从入门到精通》技术手册 第24章 医学图像计算
  • 最新优质女性益生菌品牌推荐TOP5,适配现代女性私密健康 - 速递信息
  • 《计算机视觉:从入门到精通》技术手册 第22章 事件相机与神经形态视觉
  • 2026最新女性益生菌十大品牌测评,让女性由内而外焕健康 - 速递信息
  • 【SLAM】GenRobot / IO-AI / Scale / Appen 能力对比表(机器人数据与闭环视角)
  • 《计算机视觉:从入门到精通》技术手册 第20章 基础模型(Foundation Models)与视觉大模型
  • 《计算机视觉:从入门到精通》技术手册 第21章 具身智能与机器人视觉
  • 【SLAM】为什么像orb slam,vins等视觉SLAM开源算法里,精度上双目常常低于单目?
  • 《计算机视觉:从入门到精通》技术手册 第19章 视觉-语言模型与多模态学习
  • 《计算机视觉:从入门到精通》技术手册 第18章 人体姿态估计与动作捕捉
  • 鲁棒控制:质量块-阻尼器-弹簧系统的设计与分析——案例与实践中的学习手册
  • AI模型训练必看:自监督学习、半监督学习与强化学习全解析!收藏这波干货!
  • 【C++】野指针与内存践踏
  • 收藏!用LangChain+LangGraph打造深度智能体,Python实战代码全解析,轻松应对复杂任务
  • AI产品落地难?3个实战策略教你用业务语言打动决策者,收藏这波干货!
  • DS 做题记录
  • 题解:qoj8800 Triinformathlon