go-sql-mock 仅拦截标准 database/sql 接口调用,与业务逻辑无关;必须传正确 drivername(如"mysql"),expectquery 默认全字符串匹配需严格一致,newrows 必须显式声明列名,事务需显式 expectbegin/expectcommit,结尾必调 expectationsweremet()。

Go-SQL-Mock 本身不关心你的业务逻辑是否“语言学习相关”,它只管拦截 *sql.DB 的调用并按你预设的规则返回响应。只要你的数据库操作走的是标准 database/sql 接口,mock 就能生效 —— 和你代码里是查用户表、词库表还是语义分析日志表毫无关系。
sqlmock.New() 必须传 driverName,否则 panic
常见错误是直接调用 sqlmock.New() 不带参数,结果测试一跑就 panic,报错类似 sql: unknown driver "" (forgotten import?)。
- driverName 必须和你生产代码里
sql.Open("mysql", dsn)第一个参数一致,比如"mysql"、"postgres"或"sqlite3" - 不需要真正 import 对应驱动(如
_ "github.com/go-sql-driver/mysql"),sqlmock自己注册了同名驱动 - 如果项目同时测多种数据库,得为每种 driverName 单独建 mock 实例,不能混用
ExpectQuery() 默认全字符串匹配,空格换行都算数
写 mock.ExpectQuery("SELECT * FROM words WHERE level = ?"),但实际执行的是 "SELECT * FROM words\nWHERE level = ?"?mock 直接报错:expected query, got …
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- SQL 字符串必须完全一致:空格、换行、缩进、占位符风格(
?vs$1)全都要对上 - 推荐用正则:
mock.ExpectQuery(`^SELECT \* FROM words WHERE level = \?$`),避免被格式细节卡住 - PostgreSQL 用户注意:
sqlmock默认识别$1,若代码用?,需确保两者统一,或显式调用sqlmock.NewWithDSN("postgres://")
NewRows() 必须显式声明列名,否则 Scan 失败
mock 返回 rows 后,业务代码调 rows.Scan(&id, &word, &translation) 却报 sql: expected 3 destination arguments in Scan, not 0 —— 这是因为没告诉 mock 这行数据有几列、叫什么。
- 必须用
sqlmock.NewRows([]string{"id", "word", "translation"})初始化列头 - 列名顺序要和
Scan()参数顺序严格一致,错一位值就全乱 - 如果查询可能为空,用
mock.NewRows([]string{"id"})+ 不调AddRow()即可,不用额外处理 nil
事务测试必须显式 ExpectBegin/ExpectCommit,不能只 mock Exec
在事务里调 tx.Exec("UPDATE ..."),只写 mock.ExpectExec("UPDATE ...") 是无效的 —— *sql.Tx 不经过 mock 拦截,测试会 panic 或报 sql: Tx.Exec: driver does not support tx.Exec。
- 正确流程:先
mock.ExpectBegin(),再mock.ExpectExec(...),最后mock.ExpectCommit()或mock.ExpectRollback() -
db.Begin()返回的*sql.Tx本身不被 mock,但它的Stmt().Query/Exec会回退到 underlying*sql.DB,所以仍需预设期望 - 事务中嵌套调用其他函数时,确保所有 DB 操作都通过你传入的
*sql.DB实例,而不是内部又 new 出一个
最易被忽略的其实是 mock.ExpectationsWereMet() —— 不调它,哪怕一条 SQL 没命中、一行数据没返回、一个事务没 commit,测试也照样 green。假阳性比失败更危险。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










