Go Mock测试必须基于接口,因Go无法直接mock结构体或具体类型,gomock仅支持生成接口的模拟实现;若业务依赖*sql.DB等具体类型,须先抽象为接口,且接口方法须导出、签名精确、调用顺序与Expect严格一致。

Go Mock测试必须基于接口,不是随便找个 struct 就能 mock
Go 里没法直接 mock struct 或 concrete type,gomock 只能生成接口的模拟实现。如果你的业务逻辑直接依赖 *sql.DB、http.Client 这类具体类型,就无法用标准方式打桩——得先把它抽象成接口。
常见错误是写完业务代码才发现没定义接口,临时补一个却漏掉方法签名细节。比如原接口返回 (int, error),mock 生成时只写了 int,测试跑起来会 panic。
- 定义接口时所有方法必须导出(首字母大写),
gomock不处理小写方法 - 泛型接口要显式指定类型参数,例如
Repository[T any]不能只写Repository - 生成命令必须带
-interface=YourInterfaceName,光用-source=file.go容易漏掉嵌套包里的接口
Expect() 调用顺序错一帧,整个测试就静默失败
gomock 默认按注册顺序匹配调用,不是按参数值匹配。如果真实代码先调 GetUser(1) 再调 GetUser(2),但你在测试里反着写 EXPECT,第二个调用就会报 Unexpected call 错误,而且不会告诉你哪一行出问题。
更隐蔽的是:同一方法多次调用,但只写了一个 EXPECT().Return(),后面几次调用就直接 panic,而不是走默认返回值。
立即学习“go语言免费学习笔记(深入)”;
- 用
Times(n)显式声明调用次数,比如幂等操作要测两次调用是否都成功 - 涉及循环或重试逻辑时,必须分多次写
EXPECT(),每次对应一次实际调用 - 想验证“不该被调用”,得写
EXPECT().Times(0),否则漏掉这个分支,覆盖率数字会虚高
DoAndReturn() 是覆盖 if/else 分支的唯一可靠方式
Return() 只能固定返回值,根本触发不了 if err != nil 或 if len(data) == 0 这类分支。很多测试看着过了,覆盖率却卡在 70% 上不去,就是因为所有分支都用同一个返回值硬塞。
比如一个函数内部有 if id ,你用 <code>EXPECT().Return(nil, errors.New("x")) 是没用的——它不校验 id 值,只是把错误塞回去。
- 改用
DoAndReturn(func(id int) (*User, error) { ... }),在回调里根据id动态决定返回什么 - 需要同时验证参数合法性?在
Do()里加t.Errorf,比靠 EXPECT 自动校验更直接 - 慎用
gomock.Any():它会让EXPECT().GetUser(gomock.Any())匹配任何 id,掩盖参数敏感逻辑
覆盖率报告里看不到的坑:未调用的 EXPECT 不报错
go tool cover 只统计代码行是否执行过,不关心你有没有设置 EXPECT。如果某个接口方法在真实流程里根本没被调用,但你给它写了 EXPECT(),覆盖率照样显示那行“已覆盖”——因为 EXPECT 本身是测试代码,不是被测代码。
反过来,如果某条路径里该调用却没调用,而你又没写 Times(0),gomock 默认允许未注册调用(除非启用 CallTracker),结果就是漏测且零提示。
真正危险的是:你以为覆盖了全部分支,其实只是把 EXPECT 写满了,而真实调用链压根没走到那里。


















