go-mock 不能模拟数据库驱动,因其仅支持接口且 *sql.db 是结构体、driver.conn 未导出;应使用 sqlmock 拦截并校验 sql 行为,二者职责分明:gomock mock 业务接口,sqlmock mock 数据库底层。

Go-Mock 本身不模拟数据库驱动 —— 它只 mock 接口,而 sqlmock 才是专用于模拟 sql/driver 的正确工具。 混用或误用 gomock 处理数据库调用,会导致测试无法捕获 SQL 行为、漏校验查询语句、甚至编译失败。
为什么不能用 gomock 模拟 *sql.DB 或 driver.Conn?
gomock 只能为 Go 接口生成 mock 实现,但 *sql.DB 是结构体,sql/driver.Conn 是接口却未导出(首字母小写),且其方法签名含未导出类型(如 driver.Value)。gomock 无法识别、无法生成、也无法安全调用。
-
mockgen -source=database/sql/sql.go会静默失败或报no interfaces found - 强行 mock
sql.Scanner或自定义 wrapper 接口,会导致断言与真实 SQL 执行脱节 —— 你 mock 了“返回值”,但没约束“发了什么 SQL” - 真实 DB 连接失败、SQL 拼写错误、参数绑定顺序错,在 gomock 测试里完全不可见
应该用 sqlmock 替代 gomock 处理数据库交互
sqlmock 实现了 sql/driver.Driver,能拦截所有 db.Query/db.Exec 调用,并精确校验 SQL 字符串、参数、执行次数。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 安装:
go get github.com/DATA-DOG/go-sqlmock - 初始化 mock driver:
mockDB, mock, err := sqlmock.New(),然后传给gorm.Open(sqlmock.Driver{}, mockDB)或直接sql.Open("mysql", "mock") - 预设期望 SQL:
mock.ExpectQuery(`SELECT \* FROM users WHERE id = \?`).WithArgs(123).WillReturnRows(...) - 必须在测试末尾调用:
assert.NoError(t, mock.ExpectationsWereMet()),否则 SQL 未被触发或多余调用都会失败
gomock 和 sqlmock 可以共存,但职责分明
典型分层中:gomock 负责 mock 你的业务接口(如 UserRepo),sqlmock 负责 mock 数据库底层驱动。两者不冲突,但不能越界。
- 如果你的
UserRepo是接口,且依赖*sql.DB,那就用 gomock mockUserRepo,**不碰 DB 字段**;DB 相关逻辑应下沉到另一个可被 sqlmock 替换的仓储实现里 - 如果硬要把
*sql.DB注入进被测对象,又想验证 SQL,那必须用 sqlmock —— 此时 gomock 对该字段无意义 - 常见错误:用 gomock mock 一个包装了
db.Query的方法,却不校验 SQL 内容,导致“测试通过但 SQL 写错”
真正难的不是选工具,而是决定「哪一层该由谁来 mock」:gomock 管接口契约,sqlmock 管 SQL 行为。漏掉其中任一校验点,测试就只是过家家。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










