sqlmock.ExpectQuery 匹配失败主因是SQL字面量全等匹配,需确保大小写、空格、占位符(?或$1)完全一致;推荐用正则ExpectQuery(^SELECT.*users.*id.*$);事务内Expect须严格按执行顺序;测试末必须调用ExpectationsWereMet()校验。

sqlmock.Mock.ExpectQuery 匹配不到 SELECT 语句?
常见现象是调用 db.Query() 后测试 panic,报错类似 there is no expectation for Query, SQL was: SELECT * FROM users WHERE id = ?。根本原因是 sqlmock 默认只匹配 SQL 字符串的**字面量全等**,而 Go 的 database/sql 驱动(如 mysql 或 postgres)在预处理时可能重写 SQL(比如把 ? 替成 $1),或你的代码里拼了换行/空格/别名。
实操建议:
- 用
sqlmock.New() + sqlmock.WithDSN("sqlmock")创建 mock,避免驱动干扰 - ExpectQuery 参数必须和实际执行的 SQL 完全一致:包括大小写、空格、换行、占位符符号 —— 如果用
mysql驱动,就传?;如果用postgres驱动且开了预编译,就得用$1 - 更稳妥的做法是用正则匹配:
mock.ExpectQuery(`^SELECT.*users.*id.*$`).WithArgs(123),注意用反引号包裹正则表达式
怎么模拟 QueryRow 返回 nil error 但 scan 失败?
很多逻辑依赖 err == sql.ErrNoRows 或 scan 时字段类型不匹配的错误,但直接让 Rows.Scan() 报错需要控制底层行为。
实操建议:
- 不要调用
mock.ExpectQuery().WillReturnRows()后再手动构造错误 —— sqlmock 的Rows是只读结构体,不能注入 scan 错误 - 用
mock.ExpectQuery().WillReturnError(sql.ErrNoRows)模拟查无结果 - 模拟 scan 类型错误:先调用
sqlmock.NewRows([]string{"id", "name"}),再用.AddRow(123, "foo").AddRow("not-an-int", "bar")—— 第二行的"not-an-int"在 Scan 到 int 字段时会触发sql.ErrScan
事务里嵌套 Exec 和 Query 怎么顺序 Expect?
sqlmock 严格按执行顺序校验 Expect 调用。如果你在事务里先 Exec 插入,再 QueryRow 查刚插入的 ID(比如用 RETURNING id),Expect 必须按这个顺序注册,否则报 there is no expectation for Exec 或类似错。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
实操建议:
- 事务内所有 SQL 操作,Expect 必须与代码中
tx.Exec()/tx.Query()的调用顺序完全一致 - 用
mock.ExpectBegin()开头,mock.ExpectCommit()或mock.ExpectRollback()结尾,否则事务没关闭会导致后续 Expect 失效 - 如果事务里混用了
db.Query()(非事务对象),它走的是独立连接,Expect 不受事务 Expect 影响 —— 但这种混用本身就不推荐
测试完忘记调用 mock.ExpectationsWereMet() 会怎样?
不会报错,但未满足的 Expect 会被静默忽略,导致测试“假通过”。这是最常被忽略的一步。
实操建议:
- 每个测试函数末尾必须加
assert.NoError(t, mock.ExpectationsWereMet())(用 testify/assert)或if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } - 如果想提前知道哪条 Expect 没命中,可以在
ExpectationsWereMet()报错后打印mock.Stats(),它返回已触发/未触发的 Expect 数量 - 别依赖
defer mock.ExpectationsWereMet()—— defer 在 panic 时才执行,而某些 SQL 错误会让测试提前退出,defer 不触发
sqlmock 的核心约束很硬:SQL 字符串要对得上、调用顺序要对得上、Expect 要收得干净。一旦出问题,优先检查这三点,而不是怀疑逻辑或驱动 —— 它本就不模拟数据库行为,只校验你“说要执行什么”和“实际执行了什么”是否一致。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










