源本科技 | 码上会

使用 Mockito

2026/04/01
2
0

引言

Mockito 是一款用于 Java 单元测试的开源模拟(Mock)框架,它允许开发者创建模拟对象(假对象)来模拟真实依赖的行为。通过用模拟对象替换真实对象,Mockito 可以让代码在隔离环境中进行测试,无需调用真实的数据库、第三方接口或外部服务,是 Spring Boot 项目单元测试的标配工具


核心优势

  1. 测试提速:规避数据库、网络请求等耗时操作,单元测试毫秒级执行

  2. 代码简洁:通过 @Mock@InjectMocks 等注解消除样板代码

  3. 隔离测试:只测试目标业务逻辑,不受外部依赖影响

  4. 行为验证:精准校验方法是否被调用、调用次数、调用参数

  5. 无缝整合:完美兼容 JUnit 5、Spring Boot,开箱即用


核心概念

模拟对象

模拟对象是真实对象的伪实现,专门用于测试场景。 作用:替换测试类的外部依赖(如 Service 依赖的 Mapper/Repository),让测试类独立运行。

桩实现

为模拟对象的方法预设返回值或抛出异常,定义方法的执行结果。

// 示例:调用findById(1)时,直接返回预设的用户对象
when(userRepository.findById(1)).thenReturn(user);

行为验证

校验测试过程中,模拟对象的方法是否被调用、调用次数、调用顺序,验证代码逻辑是否正确执行。

// 验证save方法被调用1次
verify(userRepository).save(user);

核心注解

注解

作用

核心说明

@ExtendWith(MockitoExtension.class)

启用 Mockito

JUnit 5 测试类必须添加,开启注解支持

@Mock

创建模拟对象

自动生成假对象,完全模拟,不调用真实方法

@InjectMocks

注入模拟对象

创建测试目标类实例,自动将 @Mock 对象注入

@Spy

创建部分模拟对象

基于真实对象,可自定义部分方法,其余调用真实逻辑


核心方法

Mockito 提供两类核心方法:桩实现(预设结果)行为验证(校验调用)

1. 桩实现方法

方法

作用

when(方法调用).thenReturn(返回值)

预设方法返回指定结果

when(方法调用).thenThrow(异常)

预设方法抛出指定异常

doReturn(返回值).when(对象).方法()

Spy 对象专用桩实现

2. 行为验证方法

方法

作用

verify(对象).方法()

验证方法被调用 1 次

verify(对象, times(n)).方法()

验证方法被调用 n 次

verify(对象, never()).方法()

验证方法从未被调用

verify(对象, atLeast(1)).方法()

验证方法至少调用 1 次

3. 参数匹配器

灵活匹配方法参数,无需固定值:

  • any():匹配任意对象

  • anyInt()/anyLong():匹配任意数字

  • anyString():匹配任意字符串

  • isNull():匹配 null

  • eq(值):精确匹配指定值

// 匹配任意ID,返回固定用户
when(userRepository.findById(anyInt())).thenReturn(user);

环境搭建

1. Spring Boot

Spring Boot 2.2+ 已自动集成 Mockito,无需手动添加依赖,仅需引入测试 starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

2. 普通 Java 项目

<!-- JUnit 5 核心依赖 -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>
<!-- Mockito 核心 -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>
<!-- Mockito + JUnit 5 整合 -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>

实战案例

场景

测试 UserService 的业务逻辑,模拟 UserRepository(数据库层),不连接真实数据库。

1. 实体类

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
}

2. Repository

import java.util.Optional;

public interface UserRepository {
    Optional<User> findById(Integer id);
    User save(User user);
}

3. Service

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    // 根据ID查询用户
    public User getUserById(Integer id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("用户不存在"));
    }

    // 新增用户
    public User addUser(User user) {
        return userRepository.save(user);
    }
}

4. 单元测试类

import org.junit.jupiter.api.Assertions;
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;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.*;

// 启用Mockito
@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    // 1. 创建模拟对象(假的Repository)
    @Mock
    private UserRepository userRepository;

    // 2. 注入模拟对象,创建测试目标实例
    @InjectMocks
    private UserService userService;

    // 测试:查询用户成功
    @Test
    void testGetUserById_Success() {
        // 预设mock行为
        User mockUser = new User(1, "张三");
        when(userRepository.findById(anyInt())).thenReturn(java.util.Optional.of(mockUser));

        // 执行测试方法
        User result = userService.getUserById(1);

        // 断言结果
        Assertions.assertEquals("张三", result.getName());
        // 验证方法调用
        verify(userRepository, times(1)).findById(1);
    }

    // 测试:用户不存在,抛出异常
    @Test
    void testGetUserById_NotFound() {
        // 预设返回空
        when(userRepository.findById(anyInt())).thenReturn(java.util.Optional.empty());

        // 断言抛出异常
        RuntimeException exception = Assertions.assertThrows(RuntimeException.class,
                () -> userService.getUserById(1));
        Assertions.assertEquals("用户不存在", exception.getMessage());
    }
}

高级知识

1. @Mock 与 @Spy

  • @Mock:完全模拟,所有方法默认不执行真实逻辑,必须手动桩实现

  • @Spy:部分模拟,默认执行真实方法,可自定义部分方法

@Spy
private UserService userService;

// Spy 桩实现写法
doReturn(new User(1,"测试")).when(userService).getUserById(1);

2. BDD 行为驱动开发(推荐)

更符合人类思维的测试写法:given(预设)→ when(执行)→ then(断言)

import static org.mockito.BDDMockito.given;

@Test
void testBDD() {
    // given 预设
    User mockUser = new User(1, "张三");
    given(userRepository.findById(1)).willReturn(java.util.Optional.of(mockUser));

    // when 执行
    User result = userService.getUserById(1);

    // then 断言
    Assertions.assertEquals("张三", result.getName());
}

3. 静态方法/私有方法

Mockito 原生不支持静态 / 私有方法 mock,需整合 PowerMock(企业常用)。


最佳实践

  1. 只测试业务逻辑:Service 层是单元测试核心,Controller/Mapper 无需 Mockito 测试

  2. 隔离依赖:所有外部依赖(数据库、Redis、第三方接口)全部用 Mock 替代

  3. 全覆盖场景:正常场景 + 异常场景 + 边界场景都要编写测试用例

  4. 不连接真实环境:单元测试必须独立运行,不依赖数据库 / 网络

  5. 行为验证:不仅校验返回值,还要校验依赖方法是否正确调用


总结

  1. 核心定位:Mockito 是 Java 单元测试的模拟框架,用于隔离外部依赖

  2. 核心注解@Mock(创建假对象)、@InjectMocks(注入依赖)、@ExtendWith(启用框架)

  3. 核心操作when().thenReturn()(预设结果)、verify()(验证调用)

  4. 核心价值:让单元测试独立、快速、稳定,是保障代码质量的核心工具

  5. Spring Boot 整合:开箱即用,无需额外配置,是企业级项目标配