绝大多数内部接口应手写fake,仅当mock外部模块接口或需严格校验调用顺序时才用gomock/mockgen。

Go语言单元测试中,Mock数据生成不是“要不要做”的问题,而是“用什么方式做、什么时候做”的问题。绝大多数内部接口,手写 fake 比用 mockgen 生成更直接、更可控、更少出错。
什么时候该跳过 mockgen 直接手写 fake
你定义的接口在自己项目里(比如 UserRepository、Notifier),且方法不超过 5 个时,mockgen 是过度工程。它会生成大量未使用的 Expect/Call/Finish 逻辑,反而增加维护成本。
- 接口变更后忘记重跑
mockgen→ 编译失败或运行时 panic - 测试只调用
GetUser,却要为DeleteUser写EXPECT()→ 测试冗余且易断 - fake 字段可导出(如
Called bool、LastID int),测试里直接assert.True(t, mock.Called),不依赖黑盒验证 - 并发场景下嵌入
sync.Mutex就能安全读写状态,不用学Times(2).MinTimes(1)这类语法
mockgen 真正该用的两个场景
只有当满足以下任一条件时,才值得引入 mockgen 工具链:
- 你要 mock 的是外部模块的接口,比如
cloud.google.com/go/storage.Client或github.com/aws/aws-sdk-go-v2/service/s3.S3Client,它们没提供可替换的 interface,且方法多、签名复杂 - 业务逻辑严格依赖调用顺序和参数匹配(例如状态机驱动的支付流程),需要
After()、AnyTimes()、DoAndReturn()等精细控制
此时必须配对使用 gomock.Controller:ctrl := gomock.NewController(t) + defer ctrl.Finish(),否则未满足的 EXPECT 会被静默忽略。
HTTP 客户端测试别碰 http.DefaultClient
硬编码 http.Get 或 new http.Client{} 是 Mock 失败的根源。CI 上随机超时、404、TLS 握手失败全由此来。
- 把 HTTP 客户端抽成字段:
Client HTTPDoer(自定义接口)或*http.Client,并在构造函数中注入 - 测试中用
httptest.NewServer启动本地 handler,传server.URL给被测 client - handler 返回 JSON 时务必用
json.NewEncoder(w).Encode(resp),避免缺失Content-Type: application/json导致解析失败 - 若无法改生产代码(如 legacy 项目),才考虑
httpmock,但它对自定义Transport无效,且不报错——这是最隐蔽的坑
数据库交互优先用 sqlmock 而非接口 mock
为 *sql.DB 手写 mock 接口(比如定义 Queryer)是低效的。真实 DB 行为涉及事务、预处理语句、Rows.Next() 循环、Scan 错误等,接口层 mock 很难覆盖。
-
sqlmock拦截的是database/sql底层交互,能准确模拟Begin→Prepare→Query→Commit全链路 - SQL 匹配要具体:用
sqlmock.QueryRow("SELECT id FROM users WHERE name = ?").WithArgs("alice"),而不是模糊匹配整个 query string - 别写
mock.ExpectQuery(".*").WillReturnRows(...)—— 这会让 SQL 变更完全逃逸测试校验
真正容易被忽略的是:sqlmock 不校验参数类型,WithArgs(123) 和 WithArgs("123") 都能通过,但真实 DB 会报错。所以参数类型必须和业务代码严格一致。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











