SQLMock结合Gorm模拟Postgres插入报查询不预期错误的解决方法
SQLMock结合Gorm测试Postgres插入操作的正确配置
触发报错的核心原因
你的测试连续匹配失败,本质是三个代码层面的错误,和regexp.QuoteMeta的转义效果无关:
- 预期类型用错:Gorm默认的Create插入操作是写操作,底层调用
Exec接口,你错用了ExpectQuery去匹配需要返回结果集的查询类操作,调用类型完全不匹配。 - 匹配字符串拼错:你把SQL语句+参数调试日志的完整输出(即
SQL xxx with args [xxx]的日志格式)当成了要匹配的SQL本身,实际执行时传给数据库的SQL根本不包含后面的args日志段,自然匹配不上。 - 参数匹配方式错:你把args的结构体打印内容塞给
regexp.QuoteMeta做正则匹配,这部分内容是SQLMock内部的调试日志格式,不是真正传给驱动的参数值,硬拼永远无法对齐。
正确的配置步骤
- 替换预期方法:用
ExpectExec替代原来的ExpectQuery,匹配无结果集的写操作。 - 提取纯SQL做正则转义:只把核心插入语句传给
regexp.QuoteMeta,删掉后面拼接的args日志文本。 - 用链式方法做参数匹配:通过
WithArgs按顺序声明所有传入的参数值,让SQLMock自动完成参数校验,不用手动匹配日志格式的结构体。 - 补全预期返回值:给Exec预期设置
WillReturnResult返回插入影响行数,否则SQLMock会判定预期未完成。 - 匹配事务语句:如果Gorm没开启
SkipDefaultTransaction配置,需要在测试预期里先匹配BEGIN,最后匹配COMMIT,否则会因为事务调用未预期报错。
修正后的测试代码示例
// 纯SQL语句,不含日志拼接内容 expectedInsertSQL := `INSERT INTO "users" ("id","name","surname","birthdate","company","custom_claims","deleted") VALUES ($1,$2,$3,$4,$5,$6,$7)` // 匹配Gorm默认开启的事务(如果开启了SkipDefaultTransaction可删掉这两行) suite.mock.ExpectBegin() suite.mock.ExpectExec(regexp.QuoteMeta(expectedInsertSQL)). // 按参数顺序声明匹配值 WithArgs( ID, // 对应$1的用户ID nil, // 对应$2的name空指针 nil, // 对应$3的surname空指针 nil, // 对应$4的birthdate空指针 nil, // 对应$5的company空指针 nil, // 对应$6的custom_claims空指针 nil, // 对应$7的deleted空值 ). // 返回插入操作的影响行数,供Gorm解析 WillReturnResult(sqlmock.NewResult(1, 1)) suite.mock.ExpectCommit() // 执行被测逻辑 user, err2 := postgres.CreateUser(users.User{ ID: ID, })
常见调试注意事项
- 不要把SQLMock打印的错误日志原文直接当匹配字符串:日志是面向人读的格式化输出,不是实际驱动执行的原始SQL/参数内容。
- 如果不需要严格校验参数值,可以省略
WithArgs链式调用,SQLMock会自动接受任意传入的参数,降低测试对参数格式的耦合。 - 注意:默认Gorm的Create是Exec类型,只有配置了插入后通过Returning子句返回全量字段时,才会走Query类调用,此时需要替换回
ExpectQuery,并配置对应字段的返回结果集。
内容的提问来源于stack exchange,提问作者Andrea Costanzo
相关产品推荐
相关产品推荐

