1. Mock概念与价值

1.1 什么是Mock

Mock(模拟对象)是在测试中用来替代真实依赖的替身对象。它不仅能模拟依赖的行为,还能记录调用信息(如调用次数、参数、顺序),并在测试结束时验证这些调用是否符合预期。

核心定义:Mock 是一种测试替身,它预先设定好期望的行为,并在测试执行后验证交互是否按预期发生。

在 Go 语言中,Mock 通常通过接口实现。被测代码依赖接口,测试时注入 Mock 实现,从而隔离外部依赖(数据库、HTTP 服务、消息队列等)。

// 定义接口
type UserRepository interface {
    GetUser(id int) (*User, error)
}

// Mock 实现
type MockUserRepository struct {
    calls []int
    mockGetUser func(id int) (*User, error)
}

func (m *MockUserRepository) GetUser(id int) (*User, error) {
    m.calls = append(m.calls, id)
    return m.mockGetUser(id)
}

func (m *MockUserRepository) AssertCalled(t *testing.T, times int) {
    if len(m.calls) != times {
        t.Errorf("expected %d calls, got %d", times, len(m.calls))
    }
}

1.2 Mock在API测试中的作用

在 API 测试中,Mock 扮演着隔离墙的角色,让测试只关注当前代码逻辑,而不受外部服务状态的影响。

作用维度 具体说明 典型场景
依赖隔离 切断被测代码与外部服务的直接连接 数据库、第三方 API、消息队列
行为控制 精确控制依赖返回的数据或错误 模拟超时、异常、边界值
状态验证 验证被测代码是否正确调用了依赖 调用次数、参数、顺序
速度提升 避免网络 I/O 和磁盘 I/O 的延迟 毫秒级返回 vs 秒级真实调用
并行能力 多个测试可同时运行,互不干扰 CI/CD 流水线中的并发测试
实战建议:在 Go API 测试中,优先对外部边界(HTTP 客户端、数据库连接、gRPC 调用)使用 Mock,内部逻辑尽量使用真实对象,避免过度 Mock 导致测试失去价值。
Mock 在 API 测试中的隔离作用 被测 API Handler 真实数据库 真实第三方 API 真实消息队列 Mock 数据库 Mock 第三方 API Mock 消息队列 ✗ 被隔离 ✓ 注入 Mock 测试只验证 Handler 逻辑,不依赖外部服务的可用性和状态

1.3 Mock与Stub/Fake的区别

很多开发者混淆 Mock、Stub、Fake 这三个概念。它们虽然都是测试替身,但职责和验证方式有本质区别。

特性 Mock Stub Fake
核心目的 验证交互行为 提供预设返回值 提供轻量级真实实现
是否验证调用 是,验证次数、参数、顺序 否,只返回数据 否,内部有真实逻辑
状态管理 无状态,只记录调用 无状态,固定返回 有状态,可读写数据
实现复杂度 中等,需记录和断言 低,只需返回固定值 高,需实现完整逻辑
典型 Go 示例 gomock、testify/mock 自定义结构体返回固定值 内存数据库、本地文件系统
适用场景 验证调用协议和协作 让被测代码走通分支 替代重量级依赖做集成
常见误区:很多开发者把 Stub 当作 Mock 使用,只关注返回值而不验证调用行为,导致测试无法捕获"漏调用"或"重复调用"的 bug。在 API 测试中,验证调用次数和参数往往比返回值更重要。
Mock vs Stub vs Fake 行为对比 测试代码 Mock 验证交互行为 Stub 提供预设返回值 Fake 轻量级真实实现 断言:调用次数、参数 验证交互协议 不验证调用 只关注返回值 内部有完整逻辑 可读写状态 选择原则:验证交互用 Mock,提供数据用 Stub,替代实现用 Fake

1.4 为什么在Go API测试中优先使用Mock

Go 语言以接口为核心的设计哲学,让 Mock 成为天然的选择。结合 Go 的测试工具链,Mock 能带来以下具体收益:

  • 编译期安全:Go 接口的静态类型检查确保 Mock 实现与真实实现签名一致,避免运行时错误。
  • 零依赖测试:Mock 让测试不依赖 Docker、数据库、网络,单条测试可在毫秒级完成。
  • 精准故障注入:可以模拟网络超时、数据库连接断开、第三方返回 500 等真实难以复现的场景。
  • 并行测试安全:Mock 对象无共享状态,go test -parallel 可安全并发执行。
  • 代码生成支持:Go 生态有 gomock、mockgen、counterfeiter 等工具,可自动生成 Mock 代码。
实战原则:在 Go API 测试中,遵循"外部依赖用 Mock,内部依赖用真实"的原则。对 HTTP 客户端、数据库驱动、gRPC 连接等边界接口使用 Mock;对项目内部的 service、util 等模块使用真实对象,避免过度 Mock 导致测试与实现过度耦合。