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

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 对象注入到被测类中。

注入规则

  1. 构造器注入(优先):匹配参数类型和数量。
  2. Setter 注入:调用 setter 方法。
  3. 字段注入(最后):直接反射注入字段。

示例

@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. 最佳实践
  1. 保持测试简洁:使用注解减少手动初始化代码。
  2. 明确依赖关系:通过 @InjectMocks 明确被测类的依赖注入方式。
  3. 避免过度 Mock:仅 Mock 外部依赖,保留核心逻辑的真实性。
  4. 结合 AssertJ:使用流式断言提高测试可读性:
    assertThat(capturedEmail.getSubject()).contains("Welcome");
    

通过合理使用 Mockito 注解,可以显著提升单元测试的编写效率和可维护性。

http://www.lryc.cn/news/535946.html

相关文章:

  • 当没有OpenGL时,Skia如何绘制?
  • SaaS+AI应用架构:业务场景、智能体、大模型、知识库、传统工具系统
  • Go 语言中如何高效地处理集合
  • 布隆过滤器到底是什么东西?它有什么用
  • 【数据结构初阶第十节】队列(详解+附源码)
  • 沪深300股指期权能对股指期货进行完全套保吗?
  • JAVA学习第三天
  • win11电脑其他WiFi可以连,只有一个WiFi连不上
  • leetcode_1760 袋子里最少数目的球
  • Python 面向对象的三大特征
  • Linux下的进程切换与调度
  • 面向对象程序设计-实验六
  • MongoDB 7 分片副本集升级方案详解(上)
  • 【工业安全】-CVE-2022-35555- Tenda W6路由器 命令注入漏洞
  • 算法分析 ——《模拟》
  • 将Sqlite3数据库挂在内存上处理
  • 前端大屏适配方案:从设计到实现的全流程指南
  • 学习总结三十二
  • 飞书专栏-TEE文档
  • linux 查看设备中的摄像头迅速验证设备号
  • 2.8 企业级训练数据构造革命:从人工标注到GPT智能标注的工业级实践指南
  • DeepSeek的蒸馏技术:让模型推理更快
  • 19.4.6 读写数据库中的二进制数据
  • 如何在 Elasticsearch 中设置向量搜索 - 第二部分
  • 【CXX-Qt】0 Rust与Qt集成实践指南(CXX-Qt)
  • C++ 设计模式-适配器模式
  • 【Elasticsearch】文本分析Text analysis概述
  • 【IDEA】2017版本的使用
  • ES6 Proxy 用法总结以及 Object.defineProperty用法区别
  • 数据结构——【二叉树模版】