sqlmock.New() 默认开启严格模式,未被 Expect 的 SQL 会 panic;需显式声明所有查询、列名、事务操作及参数匹配规则,且 Expect 必须在执行前注册。
因为 sqlmock.New() 默认开启严格模式:任何没被 Expect 过的 SQL 都会立即触发 panic,比如 panic: there is no expectation for "SELECT * FROM users"。这不是 bug,是强制你显式声明所有数据库交互的设计选择。
db.Ping() 会直接 panic,得手动写 mock.ExpectQuery("SELECT 1").WillReturnRows(...)
"SELECT id FROM users" 和 "SELECTnidnFROM users" 被视为两条不同 SQLsqlmock.AnyArg() 替代,否则匹配失败根本原因是 mock 返回的 rows 列定义和 Scan 目标不匹配。SQLMock 不做类型推导,只按你声明的列名和顺序严格校验。
sqlmock.NewRows([]string{"id", "name", "created_at"}) 显式声明列名,顺序、数量要和 rows.Scan(&id, &name, &created_at) 完全一致sqlx.StructScan,struct 字段的 db tag(如 db:"user_id")必须和 rows 列名完全相同;建议统一小写 + 下划线风格time.Now() 或 sqlmock.NewNullTime(),别传字符串,否则类型不匹配直接报错SQLMock 对事务操作默认静默,tx.Commit() 是否成功,全看你有没有提前 ExpectCommit()。没 Expect 就等于“这条语句不该发生”,直接 panic。
Begin() 都必须配对 ExpectBegin(),哪怕业务里嵌套了多个 Begin()
Commit 和 Rollback 必须二选一显式 Expect,不能漏掉db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable}) 中的隔离级别会被忽略,SQLMock 只认流程,不校验选项大概率是 Expect 注册时机错了——必须在 SQL 实际执行前注册,而不是 defer 里或函数末尾补。
立即学习“go语言免费学习笔记(深入)”;
defer mock.ExpectationsWereMet(),再写 mock.ExpectQuery(...),结果 Expect 还没注册就执行了 SQLmock.ExpectQuery().WillReturnRows() 多次,不能只写一次sqlmock.New()
最易被忽略的是列名大小写和空格——它们不像真实数据库那样宽容,差一个空格或下划线位置不对,Expect 就永远不命中。