go无内置mock框架,gomock依赖接口抽象与代码生成;mockgen失败主因是接口未导出、路径错误或包名不匹配;expect需绑定controller并调用finish(),手写mock在简单场景更高效。

Go 本身不带 Mock 框架,gomock 不是语言内置能力,而是依赖接口抽象 + 代码生成的协作机制;没定义接口、没导出、路径错或包名不匹配,mockgen 就会静默失败或报 undefined: NewMockXXX。
mockgen 命令跑不通?先检查这三件事
90% 的“生成失败”不是工具问题,而是源文件不符合约束:
-
mockgen -source=xxx.go必须指向一个含导出接口(首字母大写)的普通.go文件,不能是_test.go、不能是internal/下未显式声明路径的文件 - 接口必须在包顶层定义,不能嵌套在函数或 struct 里;方法名也必须导出(如
Get,不是get) - 如果接口在
pkg/user下,但你想把 mock 放进mocks/目录,就得加-package=mocks,且mocks/目录下要有go.mod或至少一个package mocks的文件
测试里 NewMockXXX 找不到?导入路径和包名要对齐
生成的 mock 文件能编译,不代表测试能 import —— Go 的 import 路径是模块路径 + 目录结构,不是文件名:
- 假设项目模块是
github.com/yourname/project,mock 文件生成在mocks/mock_user.go,包声明是package mocks,那测试里必须写import "github.com/yourname/project/mocks" - 别用相对路径(如
./mocks),也别漏掉模块前缀;go mod tidy不会自动补这个,得手动核对 - 如果 mock 文件和业务代码在同一个包(比如都放在
user/下),用-self_package参数,否则生成的代码会 import 自己,循环引用
EXPECT 没生效?生命周期控制比写法更重要
EXPECT() 不是“声明即生效”,它绑定在 Controller 实例上,漏掉关键两步就等于没写:
- 必须调用
defer ctrl.Finish(),否则预期不会校验,失败也不报错 -
Times(n)要显式写,比如mockSvc.EXPECT().Get(123).Return(nil, nil).Times(1);不写默认是Times(1),但多次调用就会 panic - 如果被测代码里调用了 mock 对象的多个方法,每个都要
EXPECT(),漏一个,ctrl.Finish()就会失败
手写 mock 比 mockgen 更快的三种情况
别迷信自动生成——简单场景手写两行 struct 更稳、更易读、无构建依赖:
- 接口只有 1–2 个方法,参数/返回值是基础类型(如
Get(id int) (*User, error)) - 第三方 SDK 只暴露 struct(如
*aws.Client),没给 interface,这时自己抽一个AWSClient接口再手写 fake 实现,比硬套mockgen更直接 - 需要根据输入动态返回不同 error 或 panic,比如
GetFunc func(int) (*User, error)字段,测试里直接赋值,比DoAndReturn()更直观
真正卡住人的从来不是工具命令,而是接口是否可替换、mock 实例是否真被注入到被测对象里——handler 构造时 new 了真实 service,测试里却只创建了 mock 但没传进去,整个测试就只是空转。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











