Go中模拟依赖对象测试的核心是面向接口编程与依赖注入,通过手动构造轻量Mock结构体实现,无需反射式框架,强调清晰可控。

在 Go 中模拟依赖对象测试,核心是面向接口编程 + 依赖注入,而不是靠框架“自动 mock”。Go 没有反射式动态代理(如 Java 的 Mockito),所以 mock 是轻量、显式、手动构造的——这反而是优势:清晰、可控、无魔法。
用接口抽象依赖行为
mock 的前提是把外部依赖(数据库、HTTP 客户端、消息队列等)定义为接口。Go 的接口是隐式实现的,只要结构体实现了方法集,就满足该接口。
例如,不直接依赖 *sql.DB,而是定义:
type UserRepo interface {
GetUserByID(ctx context.Context, id int) (*User, error)
SaveUser(ctx context.Context, u *User) error
}
生产代码用 postgresRepo 实现它;测试时写个 mockUserRepo 结构体,仅实现需要测的方法。
立即学习“go语言免费学习笔记(深入)”;
手动编写轻量 Mock 结构体
不需要第三方库也能高效 mock。关键点:
- 字段可导出(方便测试中设置返回值或记录调用)
- 方法体只做最小必要逻辑(如返回预设值、存参数、触发错误)
- 支持断言调用次数、参数内容(靠字段记录)
type MockUserRepo struct {
GetCalls []int // 记录被调用的 id
GetUserResp *User
GetUserErr error
}
func (m *MockUserRepo) GetUserByID(_ context.Context, id int) (*User, error) {
m.GetCalls = append(m.GetCalls, id)
return m.GetUserResp, m.GetUserErr
}
通过构造函数或字段注入 mock
避免在业务逻辑里 new 具体实现,改用依赖注入:
type UserService struct {
repo UserRepo // 接口类型,非具体实现
}
func NewUserService(repo UserRepo) *UserService {
return &UserService{repo: repo}
}
// 测试中传入 mock
func TestUserService_GetProfile(t *testing.T) {
mockRepo := &MockUserRepo{
GetUserResp: &User{Name: "Alice"},
}
svc := NewUserService(mockRepo)
u, _ := svc.GetProfile(context.Background(), 123)
if u.Name != "Alice" {
t.Fatal("expected Alice")
}
if len(mockRepo.GetCalls) != 1 || mockRepo.GetCalls[0] != 123 {
t.Fatal("GetUserByID not called with expected ID")
}
}
按需选用辅助工具(非必需)
简单场景手写 mock 更快更透明。复杂依赖或大量重复 mock 可考虑:
-
gomock:官方推荐,代码生成式(
mockgen),适合大型项目统一风格 - testify/mock:基于反射的 mock 库,语法稍简洁,但运行时开销略高、错误信息不够直观
-
go-sqlmock:专用于
database/sql,能验证 SQL 语句和参数,比手写 sql mock 稳定
注意:不要为 mock 而 mock。如果一个函数只依赖纯函数或不可变数据,直接测逻辑即可;只有涉及 I/O、网络、时间、全局状态时,才需要隔离和模拟。
基本上就这些。Go 的 mock 不是炫技,而是用最少的代码守住边界、让测试专注逻辑本身。










