go-sqlmock是go生态中测试数据库交互最直接轻量的方案,无需改业务代码或抽象接口;sqlmock.new()必须传正确drivername(如"mysql"),expectquery/expectexec默认全字符串匹配,newrows()须显式指定列名,测试末尾必须调用expectationsweremet()校验。

go-sqlmock 是目前 Go 生态中测试数据库交互最直接、最轻量的方案,不需要改业务代码结构,也不用抽象接口,只要 SQL 走的是 database/sql,它就能拦住。
sqlmock.New() 必须传对 driverName,否则直接 panic
错误现象:panic: sql: unknown driver "" (forgotten import?) 或类似 driver 未注册提示。
-
sqlmock.New()第一个参数必须是字符串,比如"mysql"、"postgres"或"sqlite3" - 这个值要和你生产代码里
sql.Open("mysql", dsn)的第一个参数严格一致 - 不需要真正 import 对应驱动(如
_ "github.com/go-sql-driver/mysql"),sqlmock 自己实现了驱动接口 - 别写成空字符串或拼错 —— 这是新手最常卡住的点
ExpectQuery 和 ExpectExec 默认全字符串匹配,空格换行都算数
错误现象:expected query "SELECT * FROM users", got "SELECT * FROM users WHERE id = ?",哪怕只多一个空格也失败。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 不要手动拼接 SQL 字符串去比对;用正则更稳:
mock.ExpectQuery(`^SELECT \* FROM users WHERE id = \?$`) - PostgreSQL 用户注意占位符风格:默认认
$1,如果你代码用?,得确保两者统一,否则匹配不上 - 用
regexp.QuoteMeta()包裹真实 SQL 可避免表名含.或-导致的正则解析错误
NewRows() 不传列名,Scan 就会崩
错误现象:sql: expected 3 destination arguments in Scan, not 0,或者字段值错位(比如 name 被扫进 id 变量)。
-
mock.NewRows([]string{"id", "name", "email"})必须显式传列名切片 - 列名顺序要和
rows.Scan(&id, &name, &email)中变量顺序完全一致 - 空结果集也要初始化列名:
mock.NewRows([]string{"id"}).AddRow()不加AddRow()也合法,但列名不能少
不调用 ExpectationsWereMet(),测试就等于没跑
这是最隐蔽的问题:SQL 没执行、期望没满足、返回值没设,测试照样 green。
- 必须在测试函数末尾加
assert.NoError(t, mock.ExpectationsWereMet())或类似校验 - 如果用了
t.Cleanup(),别把它和defer db.Close()混在一起 ——ExpectationsWereMet()要在db.Close()前调用,否则事务相关期望(如ExpectCommit())可能被跳过 - 尤其注意嵌套调用场景:DAO 方法里开了事务,但测试只 mock 了 Query,忘了
ExpectBegin()和ExpectCommit(),不校验就发现不了
真正难的不是写 mock,而是把业务里那条 SQL 真实长什么样、带什么参数、返回几列、有没有事务包裹,全部还原出来 —— 这部分信息藏在 ORM 日志、DB 代理层或调试断点里,而不是文档里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










