直接用sqlmock.New()创建mock DB可绕过真实数据库,但需确保函数不硬编码sql.Open、所有SQL路径被ExpectQuery/ExpectExec覆盖且末尾调用ExpectationsWereMet();SQLMock默认全字符串精确匹配,空格、换行、占位符风格(? vs $1)均影响匹配;PostgreSQL用户需按代码占位符风格书写ExpectQuery,MySQL用户建议用regexp.QuoteMeta构造正则提升鲁棒性;Scan和StructScan出错多因列名与顺序不匹配,须用NewRows显式声明列头并严格对齐;时间字段应传time.Time或sqlmock.NewNullTime,避免字符串导致静默失败;空结果集也需调WillReturnRows(mock.NewRows([]string{"id"}));事务测试必须配对ExpectBegin()/ExpectCommit()/ExpectRollback(),且WithinTransact()标识事务内查询;Expect注册须在SQL执行前完成,多次执行需多次Expect。

直接用 sqlmock.New() 创建 mock DB 并注入被测函数,就能绕过真实数据库——但前提是函数不硬编码 sql.Open,且所有 SQL 调用路径都被显式 ExpectQuery 或 ExpectExec 覆盖,末尾必须调 mock.ExpectationsWereMet()。
mock.ExpectQuery() 匹配失败的常见原因
SQLMock 默认做**全字符串精确匹配**,空格、换行、占位符风格(? vs $1)全算在内:
- PostgreSQL 用户:业务代码若用
$1,测试就得写mock.ExpectQuery(`SELECT * FROM users WHERE id = $1`);若代码用?,得在sqlmock.New()时加sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual) - MySQL 用户:别写
mock.ExpectQuery("SELECT * FROM users")却执行"SELECT * FROM users WHERE id = ?"——WHERE 条件差一点就 panic - 安全做法:用
regexp.QuoteMeta("SELECT * FROM users") + ` WHERE id = \?`构造正则表达式,比纯字符串更鲁棒
Scan 和 StructScan 总是出错?列名和顺序没对齐
sql.Scan 报 expected 3 destination arguments, not 2,或 sqlx.StructScan 字段为空,几乎全是列定义不匹配导致的:
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
- 必须用
mock.NewRows([]string{"id", "name", "created_at"})显式声明列头,顺序要和Scan(&id, &name, &created_at)完全一致 - 若结构体字段 tag 是
db:"user_id",列名就得写"user_id",不能写"id"或"ID" - 时间字段别传字符串,用
time.Now()或sqlmock.NewNullTime(t, true);否则Scan因类型不匹配静默失败 - 空结果集也要调
mock.ExpectQuery(...).WillReturnRows(mock.NewRows([]string{"id"})),不能漏掉NewRows调用
事务逻辑测试总卡在 Commit 不报错?Expect 没配对
tx.Commit() 成功不代表它被 mock 了——SQLMock 默认不拦截事务方法,你没 ExpectCommit() 就等于“不该发生”,一旦调用就 panic:
立即学习“go语言免费学习笔记(深入)”;
- 先调
mock.ExpectBegin(),再用mockDB.Begin()获取 mock tx - 对
tx.QueryRowContext的调用,必须单独用mock.ExpectQuery().WithinTransact() - 每个
db.Begin()都要配对mock.ExpectBegin();嵌套事务也得逐层 Expect -
db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})中的隔离级别会被忽略,只认Begin/Commit/Rollback流程
最隐蔽的坑是 mock.ExpectationsWereMet() 放在 defer 里却写晚了——Expect 必须在 SQL 执行前注册,否则根本不会命中;同一 SQL 执行多次(比如循环查用户),就得调多次 ExpectQuery().WillReturnRows()。

















