手写 Fake 是默认选项,适用于自定义内部接口,实现简单、调试方便、编译报错及时;HTTP 测试应注入 httptest.Server.URL,数据库用 sqlmock 精确匹配 SQL;gomock 仅适用于外部接口或方法超10个且需严格校验的场景。

绝大多数 Go 项目里,手写 Fake 比用 gomock 或 testify/mock 更快、更稳、更容易调试。别一上来就跑 mockgen,先看接口是不是你自己的、方法是不是真被调用了。
什么时候该手写 Fake 而不是生成 mock
手写 Fake 是默认选项,不是退而求其次的妥协。它适用于你定义的内部接口:比如 UserRepository、Notifier、PaymentService 等。
- 只需实现你真正调用的方法,不用管接口里其他“理论上存在”的方法
- 字段可导出(如
Called bool、LastTo string),测试中直接断言,不依赖AssertExpectations这类黑盒机制 - 要模拟并发行为?嵌入
sync.Mutex就行,不用学Times(3)语法 - 改了接口签名?手写
Fake编译失败立刻报错,不会漏掉或静默失效
HTTP 客户端测试别碰 http.DefaultClient
硬编码 http.Get 或直接 new(http.Client) 是 mock 失败的根源——它让测试强依赖网络、第三方服务状态,CI 上随机超时、404、SSL 错误全来了。
- 正确做法是把请求入口抽成可配置字段,测试时注入
httptest.Server.URL -
httptest.Server必须defer server.Close(),否则端口泄漏、测试串扰 - handler 中用
json.NewEncoder(w).Encode()返回响应,别用 raw string——否则Content-Type缺失导致解析失败 - 若被测代码已固定用
http.DefaultClient且无法改,才考虑httpmock;但它对自定义Transport无效,且不报错,这是最隐蔽的坑
数据库 mock 别绕开 sqlmock
sqlmock 是目前最成熟、最贴近真实 *sql.DB 行为的方案。它不 mock 接口,而是 mock database/sql 底层交互,所以能覆盖事务、预处理语句、连接池行为等。
立即学习“go语言免费学习笔记(深入)”;
- 写 SQL 匹配时别用模糊通配符(如
%SELECT%),应精确匹配实际执行的语句,避免误匹配 - 记得调用
mock.ExpectClose(),否则db.Close()会 panic - 事务测试必须显式调用
mock.ExpectBegin()和mock.ExpectCommit(),否则tx.Commit()会卡住 - 不要试图 mock
sqlx或gorm的高级封装层——它们最终都走*sql.DB,mock 底层就够了
gomock 只在特定场景下值得用
gomock 的价值只在两个明确场景:你要 mock 的接口来自外部 module(比如 cloud.google.com/go/storage.Client),且它没提供可替换的 interface;或者你的接口方法超过 10 个、变更频繁、需要严格校验调用顺序和参数匹配。
- 常见错误:用
mockgen生成一堆代码却只用其中 1 个方法;测试里反复写mockRepo.EXPECT().Get().Return(),但业务逻辑根本没用到Get的第二个参数 - 改了接口签名后忘记重跑
mockgen,导致编译失败或运行时 panic -
gomock的Ctrl.Finish()必须在测试结束前调用,否则未满足的 EXPECT 会静默忽略,掩盖逻辑缺陷
复杂点不在工具选择,而在依赖是否真的抽象成了接口、是否通过构造函数或字段注入——如果还在直接调包级变量或全局 client,所有 mock 都是空中楼阁。


















