一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Golang通过Go-SQL-Mock进行数据库逻辑隔离与语言学习单元测试

时间:2026-07-24 18:10:49 编辑:袖梨 来源:一聚教程网

sqlmock.New() 默认开启严格模式,未被 Expect 的 SQL 会 panic;需显式声明所有查询、列名、事务操作及参数匹配规则,且 Expect 必须在执行前注册。

sqlmock.New() 之后为什么一执行 SQL 就 panic?

因为 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" 被视为两条不同 SQL
  • 不确定参数(如时间戳、UUID)别硬写死,用 sqlmock.AnyArg() 替代,否则匹配失败

Scan 或 StructScan 总报 “expected 3 destination arguments, not 2” 怎么办?

根本原因是 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(),别传字符串,否则类型不匹配直接报错

事务测试里 tx.Commit() 没报错但测试失败了?

SQLMock 对事务操作默认静默,tx.Commit() 是否成功,全看你有没有提前 ExpectCommit()。没 Expect 就等于“这条语句不该发生”,直接 panic。

  • 每个 Begin() 都必须配对 ExpectBegin(),哪怕业务里嵌套了多个 Begin()
  • CommitRollback 必须二选一显式 Expect,不能漏掉
  • db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable}) 中的隔离级别会被忽略,SQLMock 只认流程,不校验选项

ExpectationsWereMet() 总是 false,但明明写了 Expect?

大概率是 Expect 注册时机错了——必须在 SQL 实际执行前注册,而不是 defer 里或函数末尾补。

立即学习“go语言免费学习笔记(深入)”;

  • 常见错误:先 defer mock.ExpectationsWereMet(),再写 mock.ExpectQuery(...),结果 Expect 还没注册就执行了 SQL
  • 同一 SQL 执行多次(比如循环查用户),就得调 mock.ExpectQuery().WillReturnRows() 多次,不能只写一次
  • 别在多个测试函数间复用同一个 mock 实例,每个测试应独立初始化 sqlmock.New()

最易被忽略的是列名大小写和空格——它们不像真实数据库那样宽容,差一个空格或下划线位置不对,Expect 就永远不命中。

热门栏目