gomock适合强契约、接口明确的依赖场景,需配合go:generate使用,严格校验调用次数与参数;testify/mock更轻量,运行时动态构造,无需代码生成;sqlmock/httpmock为领域专用方案;手动stub在简单场景下性能最优。

GoMock 适合强契约、接口明确的依赖场景
如果你的业务逻辑大量依赖外部服务(如数据库 client、第三方 API client),且这些依赖都已抽象为 Go 接口,gomock 是最稳妥的选择。它强制你面向接口编程,生成的 mock 类型严格遵循接口签名,调用次数、参数顺序、返回值都能被精确校验。
常见错误现象:mock.EXPECT().Get("key").Return("val", true) 写了但测试仍 panic,大概率是没调用 ctrl.Finish() —— 它会触发未满足期望的 fatal;或者接口方法签名改了但没重新运行 go generate,导致 mock 文件过期。
- 必须配合
go:generate使用,不能手写 mock 结构体 - 不支持对非接口类型(如 struct、func)打桩,也不能 mock 全局变量或未导出方法
- 启动耗时明显:一个含 10 个方法的接口,生成 mock 文件约 200 行,初始化
Controller有固定开销
Testify/mock 更轻量,适合快速验证行为逻辑
testify/mock 不生成代码,而是运行时动态构造 mock 对象,写法更接近“手写 stub”:定义 mock 结构体,手动实现接口,并用 On().Return() 声明行为。它没有 gomock 那套 controller 生命周期管理,调试更直观。
使用场景:HTTP handler 测试中 mock 一个简单 UserService,只关心 GetUser(id) 是否被调用、传了什么 id、返回了什么用户——不需要验证调用次数是否恰好 1 次,也不需要生成完整 mock 文件。
- 无需
go generate,改接口后测试代码几乎零维护成本 - 支持部分方法 mock(其他方法可留空或 panic),适合临时绕过复杂依赖
- 不校验未声明的方法调用,默认静默忽略;想捕获意外调用得主动加
Unexpect()
SQLMock 和 HTTPMock 是领域专用方案,别强行通用化
遇到数据库或 HTTP 调用,优先用 sqlmock 或 gock/httpmock,而不是拿 gomock 去 mock *sql.DB 或 http.Client。前者直接拦截底层连接/请求,能验证 SQL 语句结构、HTTP 方法与路径、header 是否匹配,还能控制响应延迟和错误类型。
容易踩的坑:sqlmock.New() 返回的 *sql.DB 必须传给被测代码,不能只替换 sql.Open;否则真实驱动仍会发起连接。HTTP mock 同理,必须确保被测代码用的是 mock client 实例,而非默认 http.DefaultClient。
-
sqlmock.ExpectQuery("SELECT")默认区分大小写和空格,建议用正则:ExpectQuery(`(?i)select.*from.*users`) -
gock默认禁用真实网络请求,但若测试中用了http.DefaultTransport且未显式替换,可能漏 mock 导致连通生产环境 - 二者都不适合模拟长连接、流式响应等复杂交互,此时应退回到 hand-written stub
手动 Stub 在性能敏感或简单场景下反而更可靠
当接口只有 1–2 个方法,或测试中只需覆盖 1–2 种返回组合时,直接写一个匿名结构体或本地 struct 实现接口,比引入任何框架都快、干净、无副作用。Go 编译器对这种内联实现优化极好,零反射、零 interface{} 类型擦除。
示例:svc := &stubUserService{GetUserFunc: func(id int) (*User, error) { return &User{ID: id}, nil }} —— 这行代码比 gomock 初始化快 20 倍以上,内存分配为 0。
- 无法自动校验“是否多调用了一次”,但你可以加计数器字段自己 assert
- 不适合大型接口(写起来累,易错),也不适合需要复用多个 mock 行为的场景
- 若被测函数接收的是具体类型(如
*redis.Client)而非接口,只能靠重构或monkey打补丁(后者有 runtime patch 风险,慎用)
真正难的不是选哪个框架,而是决定哪些依赖值得 mock、哪些该用真实集成(比如本地 SQLite)、哪些干脆不该出现在单元测试里。接口越薄、职责越单一,mock 的成本就越低;反之,如果一个 service 既查 DB 又发 HTTP 又写日志,先拆接口,再 mock,别一上来就配 gomock。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











