sqlmock.new() 必须传入正确的 drivername(如"mysql"),否则会 panic;expectquery() 默认全字符串匹配,需注意空格、换行和占位符;newrows() 必须显式指定列名;测试末尾必须调用 expectationsweremet() 校验。

sqlmock.New() 创建 mock DB 时必须传入 driverName
不传或传错 driverName,sqlmock.New() 会 panic,错误信息通常是 sql: unknown driver "" (forgotten import?)。这是因为 sqlmock 底层依赖 database/sql 的驱动注册机制,它需要知道你“假装”用的是哪种数据库(比如 "mysql" 或 "postgres"),哪怕实际不连库。
实操建议:
- driverName 必须和你项目里
sql.Open()用的保持一致,常见是"mysql"、"postgres"或"sqlite3" - 不需要真正 import 对应驱动包(如
_ "github.com/go-sql-driver/mysql"),sqlmock自己模拟了驱动接口 - 如果用
sqlmock.New()不带参数,会默认用空字符串 —— 这是高频翻车点
ExpectQuery() 匹配 SQL 时注意空格、换行和参数占位符
sqlmock.ExpectQuery() 默认做**完整字符串匹配**,SQL 里多一个空格、少一个换行、甚至用 ? 而不是 $1,都会导致 “expected query, got …” 错误。
实操建议:
- 用
regexp.QuoteMeta()包裹真实 SQL 再传给ExpectQuery(),可避免正则元字符干扰(比如表名含.或-) - 更稳妥的做法是直接用正则:
mock.ExpectQuery(`^SELECT \* FROM users WHERE id = \?$`).WithArgs(123) - PostgreSQL 用户注意:
sqlmock默认识别$1占位符;若代码用?,需在sqlmock.New()后调用mock.ExpectQuery().WillReturnRows(...)前,先设mock.ExpectQuery().WillReturnRows(...)并确保占位符风格统一
Rows 返回值构造容易漏掉列定义(Columns())
调用 mock.NewRows() 时只传数据不传列名,会导致 Scan() 失败,报错类似 sql: expected 3 destination arguments in Scan, not 0 —— 因为底层没告诉 sql.Rows “这行有几列、叫什么”。
实操建议:
- 必须显式调用
.AddRow()前,用mock.NewRows([]string{"id", "name", "email"})初始化列头 - 列名顺序要和
Scan()中变量顺序严格一致,否则值会错位 - 如果查询返回空结果集,用
mock.ExpectQuery(...).WillReturnRows(mock.NewRows([]string{"id"}))即可,不用加AddRow()
测试完必须调用 mock.ExpectationsWereMet()
不检查期望是否满足,mock 就算没命中的 SQL、没返回的行、没调用的 Exec,测试也照样 pass。这是最隐蔽的假阳性来源。
实操建议:
- 务必在测试函数末尾加
assert.NoError(t, mock.ExpectationsWereMet())(用 testify/assert)或require.NoError(t, mock.ExpectationsWereMet()) - 如果用了
t.Cleanup()注册清理逻辑,别把ExpectationsWereMet()放进去——它必须在所有 DB 操作执行完之后、测试结束前调用 - 多个测试共用一个 mock 实例?不行。每个测试都要独立
sqlmock.New()和独立校验
mock 不是替你写业务逻辑的,它只管“调了什么 SQL、给了什么参数、该返回什么结构”。列名对不上、占位符风格混用、忘了校验期望——这些地方一松懈,测试就形同虚设。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











