Mockito怎么用?

wen python案例 3

本文目录导读:

Mockito怎么用?

  1. 目录导读
  2. Mockito是什么?为什么必须学?
  3. 环境搭建:5分钟快速集成
  4. 核心用法:Mock、Stub与Verify
  5. 高级技巧:参数匹配器与Mockito Spy
  6. 常见问答:90%开发者遇过的坑
  7. 实战案例:测试一个订单服务
  8. SEO优化建议:搜索引擎眼中的高质量Mockito内容
  9. 总结与行动清单

Mockito实战指南:从入门到精通,单元测试的得力助手

目录导读

  1. Mockito是什么?为什么必须学?
  2. 环境搭建:5分钟快速集成
  3. 核心用法:Mock、Stub与Verify
  4. 高级技巧:参数匹配器与Mockito Spy
  5. 常见问答:90%开发者遇过的坑
  6. 实战案例:测试一个订单服务
  7. SEO优化建议:搜索引擎眼中的高质量Mockito内容

Mockito是什么?为什么必须学?

问答环节
Q: Mockito能替代真实数据库吗?
A: 不能,Mockito模拟的是对象行为,而非数据存储,它适用于测试业务逻辑,比如验证“当用户余额不足时,是否抛异常”,而不是测试数据库连接。

核心概念
Mockito是Java生态中最流行的Mock框架(GitHub 14k+ Stars),它的核心作用是:

  • 隔离依赖:在单元测试中,模拟外部服务(如数据库、REST API)的返回值
  • 验证行为:检查某个方法是否被调用、调用次数、调用参数
  • 提升速度:无需加载Spring上下文、连接数据库即可运行测试

对比其他框架
| 框架 | 适用场景 | 学习曲线 | |------|----------|----------| | Mockito | 纯Java单元测试 | ★☆☆☆☆ | | EasyMock | 较老项目 | ★★☆☆☆ | | PowerMock | 需模拟静态方法 | ★★★☆☆ |


环境搭建:5分钟快速集成

Maven依赖(推荐)

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.12.0</version> <!-- 2025年最新稳定版 -->
    <scope>test</scope>
</dependency>

Gradle依赖

testImplementation 'org.mockito:mockito-core:5.12.0'

踩坑提示

  • 如果使用JUnit 5,需额外引入 mockito-junit-jupiter
  • 若发现 MockitoAnnotations.openMocks(this) 报错,请检查Mockito版本与Java版本兼容性(Mockito 5.x 要求 Java 11+)

核心用法:Mock、Stub与Verify

1 创建Mock对象

// 方式1:注解(推荐)
@Mock
UserService userService;
@BeforeEach
void setUp() {
    MockitoAnnotations.openMocks(this);
}
// 方式2:直接创建
UserService userService = Mockito.mock(UserService.class);

2 定义Stub(打桩)

// 当调用findUser(1)时,返回一个模拟用户
when(userService.findUser(1)).thenReturn(new User(1, "Alice"));
// 带异常的场景
when(userService.deleteUser(anyInt())).thenThrow(new RuntimeException("删除失败"));

3 Verify验证

// 验证findUser方法被调用了一次
verify(userService, times(1)).findUser(1);
// 验证从未调用
verify(userService, never()).findUser(anyInt());

关键区别

  • when().thenReturn() 是“设定期望行为”
  • verify() 是“事后检查是否按预期执行”

高级技巧:参数匹配器与Mockito Spy

1 参数匹配器(Argument Matchers)

// 匹配任何整数
when(userService.findUser(anyInt())).thenReturn(mockUser);
// 匹配特定类型的任意值
verify(userService, atLeastOnce()).findUser(argThat(id -> id > 0));

2 Mockito Spy(部分模拟)

// Spy在不覆盖时使用真实方法,Mock则全部模拟
List<String> list = new ArrayList<>();
List<String> spyList = Mockito.spy(list);
spyList.add("A");
when(spyList.size()).thenReturn(100); // 覆盖真实方法
System.out.println(spyList.get(0)); // 输出 "A"(真实行为)
System.out.println(spyList.size()); // 输出 100(Mock行为)

使用场景

  • 遗留代码中,只想Mock某几个方法,其余保持真实逻辑
  • 测试时需观察内部状态变化(如缓存更新)

常见问答:90%开发者遇过的坑

Q1: Mockito无法mock静态方法怎么办?

A: Mockito 3.4.0+ 支持Mock静态方法(需添加 mockito-inline 依赖)。

try (MockedStatic<MathUtils> mocked = Mockito.mockStatic(MathUtils.class)) {
    mocked.when(() -> MathUtils.add(1, 2)).thenReturn(10);
    assertEquals(10, calculator.calculate());
}

Q2: 模拟的List调用add()后,验证失败?

A: 验证的是Mock对象上的方法调用,而非原List,确保使用的是同一个Mock对象。

List<String> mockList = mock(List.class);
mockList.add("A");
verify(mockList).add("A"); // 正确

Q3: 如何重置Mock状态?

reset(userService); // 清空所有stub和调用记录(慎用!破坏测试独立性)

Q4: Mockito和Spring Boot Test如何配合?

原理

  • 在集成测试中,用 @MockBean 替换Spring上下文中的真实Bean
  • 避免加载整个ApplicationContext(可结合 @WebMvcTest 切片测试)

实战案例:测试一个订单服务

待测类(OrderService)

public class OrderService {
    private final PaymentClient paymentClient;
    private final UserRepository userRepository;
    public String createOrder(int userId, double amount) {
        User user = userRepository.findById(userId);
        if (user.getBalance() < amount) {
            throw new IllegalArgumentException("余额不足");
        }
        return paymentClient.charge(userId, amount);
    }
}

编写测试(关键步骤)

@ExtendWith(MockitoExtension.class) // JUnit 5集成
class OrderServiceTest {
    @Mock
    PaymentClient paymentClient;
    @Mock
    UserRepository userRepository;
    @InjectMocks  // 自动将mock注入到OrderService中
    OrderService orderService;
    @Test
    void testCreateOrder_Success() {
        // 准备
        User user = new User(1, 500.0);
        when(userRepository.findById(1)).thenReturn(user);
        when(paymentClient.charge(1, 200.0)).thenReturn("PAY-001");
        // 执行
        String result = orderService.createOrder(1, 200.0);
        // 验证
        assertEquals("PAY-001", result);
        verify(paymentClient, times(1)).charge(1, 200.0);
    }
    @Test
    void testCreateOrder_InsufficientBalance() {
        User user = new User(1, 100.0);
        when(userRepository.findById(1)).thenReturn(user);
        assertThrows(IllegalArgumentException.class,
            () -> orderService.createOrder(1, 200.0)
        );
        verify(paymentClient, never()).charge(anyInt(), anyDouble());
    }
}

测试结果:

  • 第一个测试验证:余额充足时,支付接口被调用,并返回正确订单号
  • 第二个测试验证:余额不足时,抛出异常,且支付接口从未被调用

SEO优化建议:搜索引擎眼中的高质量Mockito内容

1 关键词布局(自然融入)

  • 主关键词:Mockito怎么用、Mockito教程、Mockito单元测试
  • 长尾关键词:Mockito Verify用法、Mockito Spy与Mock区别、Mockito静态方法Mock
  • 优化策略:在H2/H3标题、段落首句、代码注释中自然出现,避免堆砌

2 结构化数据(Schema.org)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": "Mockito实战指南:从入门到精通,单元测试的得力助手",
  "author": "技术团队",
  "datePublished": "2025-04-15",
  "description": "全方位解析Mockito核心用法、高级技巧与常见坑,包含实战案例与SEO优化建议。"
}
</script>

3 内容更新频率

  • Mockito版本更新:每6个月检查一次Github Release(如v5.x新增功能)
  • 问答板块:根据Stack Overflow高频问题(如“Mockito静态方法时ClassNotFoundException”)持续迭代

4 外部链接策略

  • 权威来源:官网(github.com/mockito/mockito)、Baeldung教程
  • 避免死链:所有外链建议使用 rel="nofollow",但内部链接(如其他技术文章)使用 dofollow

总结与行动清单

掌握程度 技能点 预计耗时
入门 创建Mock、定义Stub、Verify 1小时
进阶 参数匹配器、Spy、Answer接口 2小时
实战 结合Spring Test、静态方法Mock 3小时

立即开始

  1. 在现有项目中添加Mockito依赖
  2. 选一个现有的Service类,写第一个Mockito测试(只需测试一个分支)
  3. 运行测试,体验“无需数据库、速度提升10倍”的快感

本文深入解析Mockito的完整用法,涵盖从环境搭建到高级技巧、从常见坑到SEO优化,希望成为开发者手边最实用的Mockito参考手册。

抱歉,评论功能暂时关闭!