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

单元测试-junit5的spy部分mock

使用 JUnit 5 进行 Spy 和 Mock 测试

在 JUnit 5 中,结合 Mockito 库可以轻松实现 Spy(部分模拟)和 Mock(完全模拟)的功能。Mockito 提供 @Mock@Spy 注解,配合 JUnit 5 的扩展机制,能高效完成单元测试。

依赖配置

确保项目中包含以下依赖(以 Maven 为例):

<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.8.2</version><scope>test</scope>
</dependency>
<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>4.5.1</version><scope>test</scope>
</dependency>
<dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><version>4.5.1</version><scope>test</scope>
</dependency>

Mock 对象的使用

@Mock 用于创建完全模拟的对象,所有方法默认返回空值或默认值,除非显式定义行为。

import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.extension.ExtendWith;@ExtendWith(MockitoExtension.class)
public class MockTest {@Mockprivate List<String> mockedList;@Testvoid testMockBehavior() {mockedList.add("item");verify(mockedList).add("item"); // 验证方法调用when(mockedList.size()).thenReturn(100); // 定义返回值assertEquals(100, mockedList.size());}
}

Spy 对象的使用

@Spy 用于创建部分模拟对象,保留真实对象的行为,仅对特定方法进行模拟。

@ExtendWith(MockitoExtension.class)
public class SpyTest {@Spyprivate List<String> spiedList = new ArrayList<>();@Testvoid testSpyRealMethod() {spiedList.add("real-item");assertEquals(1, spiedList.size()); // 调用真实方法}@Testvoid testSpyMockedMethod() {doReturn(100).when(spiedList).size(); // 模拟方法assertEquals(100, spiedList.size());}
}

关键区别与注意事项

  • Mock:所有方法均需手动定义行为,未定义的方法返回默认值(如 null0)。
  • Spy:基于真实对象,仅对需要的方法进行覆盖,未模拟的方法执行真实逻辑。
  • 初始化@Spy 需依赖真实对象实例(如直接赋值或通过 @BeforeEach 初始化),而 @Mock 自动生成空实例。
  • 语法差异
    • Mock 的存根语法:when(mock.method()).thenReturn(value)
    • Spy 的存根语法:doReturn(value).when(spy).method()

常见场景示例

验证方法调用次数:

@Test
void verifyInteraction() {spiedList.add("item");verify(spiedList, times(1)).add(anyString());
}

模拟异常抛出:

@Test
void mockException() {when(mockedList.get(0)).thenThrow(new RuntimeException());assertThrows(RuntimeException.class, () -> mockedList.get(0));
}

非注解方式实现 Spy

直接通过 Mockito.spy() 方法创建 spy 对象:

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;class ManualSpyTest {@Testvoid testManualSpy() {List<String> realList = new ArrayList<>();List<String> spyList = spy(realList); // 手动创建 spyspyList.add("real");spyList.add("data");// 部分模拟:size 方法返回固定值when(spyList.size()).thenReturn(100);assertEquals(2, spyList.size()); // 实际返回 100assertTrue(spyList.contains("real")); // 真实调用}
}

关键区别与注意事项

  1. 注解方式
    依赖 @ExtendWith(MockitoExtension.class),通过 @Spy 自动初始化 spy 对象。需手动实例化被 spy 的对象(如 new ArrayList<>()),否则会抛出异常。

  2. 非注解方式
    通过 Mockito.spy() 手动创建 spy,适合需要动态控制 spy 对象的场景。

  3. 部分模拟行为
    默认调用真实方法,仅对显式声明的方法进行模拟。以下代码会调用真实方法:

// 未模拟的方法会执行真实逻辑
spyList.add("test"); 
  1. 避免 final 方法
    Mockito 无法 spy final 方法或类(如 String),需结合 PowerMock 等工具扩展。

常见问题解决

  1. 初始化问题
    注解方式中,若 @Spy 对象未实例化会报错:
@Spy // 错误!未初始化
private List<String> spyList; @Spy // 正确
private List<String> spyList = new ArrayList<>();
  1. 静态方法模拟
    Spy 不适用于静态方法,需配合 mockStatic(Mockito 3.4+):
try (MockedStatic<MyClass> mocked = mockStatic(MyClass.class)) {mocked.when(MyClass::staticMethod).thenReturn("mocked");
}

文章转载自:

http://xDIhT6hS.rkwjs.cn
http://ZNaC8YZC.rkwjs.cn
http://CgsTLNqP.rkwjs.cn
http://vpxkeOpZ.rkwjs.cn
http://0Me4uy3L.rkwjs.cn
http://1twtPdmy.rkwjs.cn
http://pMiReJd6.rkwjs.cn
http://hqePNKtX.rkwjs.cn
http://4YuAbQ6r.rkwjs.cn
http://GCiazAle.rkwjs.cn
http://g1YlfLMQ.rkwjs.cn
http://1bVHQpX3.rkwjs.cn
http://CBGi3MG4.rkwjs.cn
http://OZ6ppqq1.rkwjs.cn
http://LvUK9ZXp.rkwjs.cn
http://6MAJL6iW.rkwjs.cn
http://kF0P3IWz.rkwjs.cn
http://mPUb6hZP.rkwjs.cn
http://KYY2fnox.rkwjs.cn
http://X5vIfz6D.rkwjs.cn
http://BxEAT9n9.rkwjs.cn
http://AKWZNHAU.rkwjs.cn
http://qD2zO7VX.rkwjs.cn
http://7I5JQEz3.rkwjs.cn
http://CZDWmxoC.rkwjs.cn
http://qC7dyU6m.rkwjs.cn
http://Xp6yHRhK.rkwjs.cn
http://nSStw4Dr.rkwjs.cn
http://XkE4FSIZ.rkwjs.cn
http://qS0HE1vc.rkwjs.cn
http://www.dtcms.com/a/376631.html

相关文章:

  • 新能源汽车车载传感器数据处理系统设计(论文+源码)
  • 基于安全抽象模型(SAM)的汽车网络安全防御与攻击分析
  • 【qt】通过TCP传输json,json里包含图像
  • 力扣每日一刷Day 20
  • 线程池队列与活跃度报警检测器实现详解
  • 【硬件-笔试面试题-80】硬件/电子工程师,笔试面试题(知识点:MOS管与三极管的区别)
  • A股大盘数据-20250910分析
  • 大数据毕业设计-基于大数据的健康饮食推荐数据分析与可视化系统(高分计算机毕业设计选题·定制开发·真正大数据)
  • 墨水屏程序
  • 小米自带浏览器提示“已停止访问该网页”的解决办法以及一些优化
  • 零代码入侵:Kubernetes 部署时自动注入 kube-system UID 到 .NET 9 环境变量
  • Python核心技术开发指南(049)——文件操作综合应用
  • 机器学习项目中正确进行超参数优化:Optuna库的使用
  • QueryWrapper 全面解析:从原理到实战
  • 2025时序数据库选型:深入解析IoTDB从主从架构基因到AI赋能的创新之路
  • 云手机可以用来托管游戏吗?
  • 每日算法之:给定一个有序数组arr,代表坐落在X轴上的点,给定一个正数K,代表绳子的长度,返回绳子最多压中几个点? 即使绳子边缘处盖住点也算盖住
  • 如何利用AI工具更好地服务人:从效率到温度的平衡
  • ADC模数转换器详解(基于STM32)
  • 深入理解网络浏览器运行原理
  • 线扫相机不出图原因总结
  • 【Linux系统】日志与策略模式
  • 物联网时序数据库IoTDB是什么?
  • Rust:系统编程的革新者
  • 【postMan / apifox 文件上传】
  • 使用 javax.net.ssl.HttpsURLConnection 发送 HTTP 请求_以及为了JWT通信选用OSS的Jar的【坑】
  • 9.10 Swiper-layer-laydate
  • 基于代理模式:深入了解静态代理和动态代理
  • 崔传波教授:以科技与人文之光,点亮近视患者的清晰视界‌
  • java 代理模式实现