使用go-sqlmock测试Gorm时查询语句匹配失败问题排查
Go-Sqlmock测试Gorm时SQL语句不匹配的正确解决方法
问题根源
从报错信息可以明确看到,Gorm实际生成的SQL是:
SELECT * FROM `transaction` WHERE id = ?
而你测试中预期的正则对应的SQL是:
SELECT * FROM transaction WHERE id = ?;
两者核心差异有两点:
- 由于
transaction是MySQL保留关键字,Gorm会自动给表名加上反引号进行转义 - 实际生成的SQL末尾没有分号,但你预期的语句带了分号
正确匹配方案
不要用regexp.QuoteMeta直接转义整个SQL字符串,而是编写和实际生成SQL完全匹配的正则表达式:
修改测试代码中的ExpectQuery部分
rows := sqlmock.NewRows([]string{"id", "buyer_id", "seller_id", "item_id", "message_id", "expired_date", "created_at"}). AddRow(2, 2, 5, 2, 2, "2022-09-10 01:01:00", "2022-09-08 01:01:00") // 正则里的反引号需要转义,同时去掉末尾的分号 mock.ExpectQuery(regexp.MustCompile(`SELECT \* FROM \`transaction\` WHERE id = \?`)).WillReturnRows(rows)
小技巧:提前获取Gorm生成的准确SQL
如果不确定Gorm生成的SQL格式,可以临时在待测试函数中添加DryRun代码打印SQL:
func (rc repositoryClient) GetTransactionByID(id int) (domain.Transaction, error) { trans := domain.Transaction{} // 临时添加DryRun打印SQL stmt := rc.db.Where("id = ?", id).Find(&trans).Statement fmt.Println(stmt.SQL.String()) // 控制台会输出准确的SQL语句 status := rc.db.Where("id = ?", id).Find(&trans) // ... 原有逻辑 }
运行测试后,直接复制控制台输出的SQL,再转成对应的正则即可(只需要转义反引号、问号这类正则特殊字符)。
为什么之前的方法失效
regexp.QuoteMeta会把所有正则特殊字符(包括反引号)都转义,导致你预期的正则中没有表名的反引号;同时你预期的SQL末尾带了分号,而Gorm实际生成的SQL没有分号,这两个点共同导致了匹配失败。
额外注意点
你定义的successGetTransaction中ExpiredDate的值"2022-09-010 01:01:00"存在日期格式错误(多了一个0),建议修正为"2022-09-10 01:01:00",否则即使SQL匹配成功,断言也会失败。
内容的提问来源于stack exchange,提问作者mrpepo877
相关产品推荐
相关产品推荐

