mock只适用于接口而非框架,go无框架级mock模块;必须先有清晰可导出的interface才能用gomock生成mock,否则会报undefined或类型不匹配错误。

mock 不是给框架用的,是给接口用的
Go 没有“框架级 mock 模块”这种东西。所谓“快速搭建外包接口契约测试”,核心不是选哪个 mock 工具,而是先确认:你正在对接的外包服务是否已定义清晰、可导出的 interface?如果没有,gomock、testify/mock 都会卡在第一步——生成失败或编译报错。
常见错误现象:undefined: NewMockExternalService 或 cannot use mockX as type ExternalService。根本原因不是工具没装好,而是业务代码直接调用了外包 SDK 的结构体方法(比如 client.DoRequest()),而不是通过 interface 依赖注入。
- 外包 SDK 提供了 interface?优先用它,
mockgen -source=vendor/xxx/sdk.go直接生成 - 没提供 interface?别硬套 gomock,手写 fake 更快:
type FakePaymentClient struct { DoFunc func(req PaymentReq) (PaymentResp, error) } - 连 SDK 都没给,只有 HTTP 文档?跳过接口 mock,直接用
httptest.Server模拟响应体和状态码
mockgen 命令必须带 -source,且路径要精确到 .go 文件
mockgen 不会扫描目录或识别 internal/ 包,-source 参数必须指向一个具体含 interface 定义的 Go 源文件,否则生成空文件或报错 no interfaces found。
典型正确写法:mockgen -source=external/payment.go -destination=mocks/mock_payment.go -package=mocks
-
-source不能是目录、不能是通配符(如*.go)、不能是_test.go文件 -
-package必须与-destination所在目录名一致,否则测试文件 import 时提示no required module provides package - 如果 interface 在
internal/api/下,加-self_package=yourmodule/internal/api显式声明 - 生成后检查 mock 文件是否包含
NewMockXXX(ctrl *gomock.Controller)构造函数,没有说明路径或包名不匹配
测试里不调 ctrl.Finish() = 白测
ctrl.Finish() 不是收尾仪式,它是验证开关。漏掉它,所有 EXPECT() 都不会触发检查,测试“成功”但实际没调用目标方法,或调用参数错、次数错、顺序错,全被忽略。
正确模板:
ctrl := gomock.NewController(t)
defer ctrl.Finish() // 必须放 defer,保证 panic 时也执行
mockCli := mocks.NewMockPaymentClient(ctrl)
mockCli.EXPECT().Charge(gomock.Eq("order-123"), gomock.Any()).Return(&ChargeResp{ID: "ch_abc"}, nil)
// 调用被测逻辑...
- 每个测试函数必须独立
NewController,复用ctrl会导致期望状态污染 -
.EXPECT().Method()后必须跟.Return()或.Do(),否则运行时 panic:missing return value for method "Charge" - 参数匹配别直接传字面量,用
gomock.Eq(100)或gomock.Any(),否则类型不匹配报错
HTTP 外包接口,优先用 httptest.Server 而非 gomock
对外包 HTTP 接口做契约测试,重点是响应格式、状态码、Header、超时行为,不是校验“调用了几次 Do()”。用 httptest.Server 更轻、更真实、无需改业务代码。
关键点:
- 把请求客户端抽成字段(如
Client *http.Client和BaseURL string),测试时注入server.URL - handler 中用
json.NewEncoder(w).Encode(resp),别用fmt.Fprint(w, `{"id":"x"}`),否则缺失Content-Type导致前端解析失败 - 用
curl -v http://localhost:port/contract先验证响应结构,再写测试 - 若业务代码已硬编码
http.DefaultClient且无法改,才考虑httpmock,但它对自定义Transport无效且静默失败——这是最隐蔽的坑
契约测试最难的不是生成 mock,而是保持 mock 行为与外包文档严格同步。每次外包更新 API,mock 的 JSON 结构、错误码、字段必填性都得跟着动,手动维护比自动生成更可靠。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











