sqlmock 是严格校验 sql 调用的测试工具,需显式声明 expectquery/expectexec、匹配 sql 字面量、参数、列名及事务流程,并调用 expectationsweremet() 验证。

SQLMock 不是“让测试跑起来就行”的工具,它默认严格校验每一条 SQL 调用;没 ExpectQuery 或 ExpectExec 就执行,立刻 panic。
sqlmock.New() 后必须注入 mock *sql.DB,不能在函数里自己 sql.Open
业务代码若硬编码 sql.Open("postgres", dsn),测试时会连真实库或报 unknown driver "sqlmock"。SQLMock 的驱动名是 "sqlmock",不是 "postgres"。
- 测试文件顶部必须加
_ "github.com/DATA-DOG/go-sqlmock"(下划线触发 init 注册) - 用
db, mock, err := sqlmock.New()创建 mock 实例,然后传给被测函数(比如CreateUser(db, ...)) - 禁止在测试中写
sql.Open("postgres", "fake://")—— 它仍会尝试解析并初始化驱动,可能触发网络或 panic
ExpectQuery/ExpectExec 必须匹配 SQL 字符串全貌,空格换行都不能差
SQLMock 默认做**字面量精确匹配**,不归一化、不忽略 WHERE、不处理占位符差异。写 mock.ExpectQuery("SELECT id FROM users"),但实际执行的是 "SELECT id FROM users WHERE name = ?",就直接 panic。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用正则需显式包裹,例如
mock.ExpectQuery(regexp.QuoteMeta("SELECT id FROM users") + `.*`) - WHERE 条件、ORDER BY、换行缩进都要和代码里生成的 SQL 一模一样;建议在 DAO 层固定 SQL 格式,避免拼接时多空格
-
WithArgs(...)必须和实际传入db.Query(...)的参数顺序、数量、类型一致;nil值要用sqlmock.NullString{Valid: false}等对应类型
Scan 和 StructScan 失败?列名、顺序、类型三者必须对齐
sql.Scan 报 expected 3 destination arguments, not 2,或 StructScan 字段为空,几乎全是列定义不匹配导致的。
- 用
sqlmock.NewRows([]string{"id", "name", "created_at"})显式声明列名,顺序和Scan(&id, &name, &created_at)严格一致 - 若用
sqlx.StructScan,列名必须和 struct tag(如db:"created_at")完全小写匹配;别混用json:"created_at"和db:"id" - 时间字段别传字符串,用
time.Now()或sqlmock.NewNullTime(t, true);否则 scan 因类型不匹配静默失败
事务测试必须 ExpectBegin + ExpectCommit/ExpectRollback,且顺序不能乱
tx.Commit() 或 tx.Rollback() 不触发 panic 并不等于它被 mock 了——SQLMock 默认不拦截事务方法,你没 Expect 就等于“不该发生”,一旦调用就 panic。
- 每个
db.Begin()都要配mock.ExpectBegin();嵌套事务也要逐层ExpectBegin() -
tx.QueryRowContext()这类调用,得用mock.ExpectQuery(...).WithContext(ctx),否则不匹配 - 测试末尾必须调
mock.ExpectationsWereMet();漏掉这句,哪怕所有 SQL 都没执行,测试也“成功”通过
最易被忽略的点:SQLMock 不校验事务隔离级别、不解析 SQL 语义、不验证表结构。它只认“你声明了什么 SQL,传了什么参数,按什么顺序调用了什么方法”。想测字段类型映射或索引行为,该换 SQLite 内存模式上真实 SQL 引擎。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










