SQLMock 默认严格模式,未预设SQL即panic;需显式Expect所有查询、事务及健康检查,列名/类型/上下文须严格匹配,Expect须在执行前注册。

SQLMock 初始化后查不到表就 panic?
SQLMock 默认严格模式,只要执行了没被预设的 SQL,立刻抛 panic: there is no expectation for "SELECT * FROM users"。这不是 bug,是设计使然——它强制你显式声明所有数据库交互。
- 初始化时用
sqlmock.New()即开启严格模式;想跳过未定义语句(不推荐),得加选项sqlmock.Option.IgnoreUnexpectedQuery - 表名、字段名、WHERE 条件里的空格和换行必须完全匹配,SQLMock 不做格式化归一化;建议用
sqlmock.AnyArg()替代不确定值 - 别在测试里调
db.Ping()—— 它会触发真实连接检查,而 SQLMock 的*sql.DB不支持;改用mock.ExpectQuery("SELECT 1").WillReturnRows(...)模拟健康检查
怎么让 Scan 或 StructScan 正常工作?
SQLMock 返回的 mock rows 必须和你的 scan 目标类型严格对齐,否则 sql.Scan 报 sql: expected 3 destination arguments in Scan, not 2 这类错。
- 用
mock.NewRows([]string{"id", "name", "created_at"})显式声明列名,顺序、数量必须和Scan(&id, &name, &created_at)一致 - 如果用了
sqlx.StructScan,列名要和 struct tag(如json:"user_id")或字段名完全对应;建议统一用 db tag 如db:"id"并保持小写 - 时间字段别传字符串,用
time.Now()或sqlmock.NewNullTime(...);否则 scan 会因类型不匹配失败
事务测试中 Commit 失败但没报错?
SQLMock 对事务操作默认静默,tx.Commit() 成功与否全靠你手动 Expect。没 Expect 就等于“这条语句不该发生”,直接 panic。
- 开启事务必须配对:
mock.ExpectBegin()+mock.ExpectCommit()或mock.ExpectRollback() - 如果业务逻辑里有嵌套事务(比如用
sql.Tx手动控制),每个Begin()都得 Expect,哪怕只是空事务 - 注意:SQLMock 不校验事务隔离级别,
db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})中的选项会被忽略,只认 Begin/Commit/Rollback 流程
测试完发现 mock.ExpectationsWereMet() 总是 false?
不是漏写 Expect,而是 Expect 写早了、写重了,或者被 defer 干扰了执行顺序。
立即学习“go语言免费学习笔记(深入)”;
- Expect 必须在实际 SQL 执行前注册;常见错误是在
defer mock.ExpectationsWereMet()后才写mock.ExpectQuery(...) - 同一 SQL 被执行多次(比如循环查用户),就得调多次
mock.ExpectQuery(...).WillReturnRows(...),不能只写一次 - 如果用了
db.QueryRowContext却 Expect 了QueryRow(无 context 版),会匹配失败;SQLMock 区分带 context 和不带 context 的方法调用
最麻烦的是日志和中间件悄悄触发了额外查询,比如 ORM 的 debug 日志开了,或某层自动做了 SELECT version()。打开 mock.ExpectQuery("").WillReturnRows(...) 临时捕获未知语句,再逐个补 Expect。










