使用go-sqlmock模拟查询时的结果异常与条件校验问题
解决go-sqlmock模拟查询的两个问题
问题1:预期返回最大tid值3,实际返回1
你的测试代码里,虽然给mock结果集添加了1和3两行,但QueryRow只会读取结果集的第一行。而原查询已经通过ORDER BY tid DESC LIMIT 1确保只返回最大的tid,所以mock无需模拟多行,直接返回目标值3即可。
修改测试代码中的结果集部分:
rows := sqlmock.NewRows([]string{"tid"}).AddRow(3) // 仅添加最大的tid行
另外注意:你当前用字符串"1"、"3"作为行数据,但目标变量是int类型,直接传入int值更严谨,避免潜在类型转换问题。
问题2:出现sql: expected 2 destination arguments in Scan, not 1错误
这个错误的核心是查询语句返回的列数和Scan传入的参数数量不匹配:
- 如果你修改了查询语句,比如同时查询
tid和cmdline列,但Scan时只传了&id一个参数,就会触发该错误; - 若不需要
cmdline数据,保持原查询只选择tid即可; - 若确实需要获取
cmdline,需定义对应变量并传入Scan:var id int var cmdline string err := row.Scan(&id, &cmdline)
关于cmdline LIKE条件的校验优化
当前测试代码已能匹配SQL语句,但建议用参数化查询替代字符串拼接,既避免SQL注入风险,也让mock更灵活:
// 待测试函数改为参数化查询 func (y *YumDatabase) getTransactionId(pkg string) (int, error) { var id int queryForTid := "SELECT tid FROM trans_cmdline WHERE cmdline LIKE $1 ORDER BY tid DESC LIMIT 1" row := y.db.QueryRow(queryForTid, "%install "+pkg+"%") // ... 后续逻辑不变 } // 测试代码匹配参数化SQL并设置参数 func TestGetTransactionId(t *testing.T) { // ... 初始化mock逻辑不变 rows := sqlmock.NewRows([]string{"tid"}).AddRow(3) mock.ExpectQuery(`SELECT tid FROM trans_cmdline WHERE cmdline LIKE \$1 ORDER BY tid DESC LIMIT 1`). WithArgs("%install "+pkg+"%"). WillReturnRows(rows) // ... 断言逻辑不变 }
内容的提问来源于stack exchange,提问作者Anthony Quinn
相关产品推荐
相关产品推荐

