使用sqlmock模拟gorm INSERT查询遇阻,寻求技术解决方案
解决sqlmock模拟gorm INSERT操作报错的问题
我之前在测试gorm的Create方法时也踩过这个一模一样的坑!SELECT测试跑起来完全正常,但一到INSERT就报错,看了gorm的调试日志才搞明白问题出在哪——gorm在执行INSERT时会自动加上RETURNING "groups"."id"子句(尤其是在PostgreSQL这类支持RETURNING语法的数据库中),而我们用sqlmock的时候很容易搞错监听的方法。
问题根源
gorm的Create方法在需要获取插入后的自增ID时,底层不是调用Exec,而是调用Query(因为要通过RETURNING返回结果)。如果我们用sqlmock.ExpectExec来监听这个操作,就会出现不匹配的报错,因为实际执行的是Query而非Exec。
解决方案
把ExpectExec换成ExpectQuery,并且匹配完整的SQL语句(包括后面的RETURNING部分),同时模拟返回包含ID的结果集。
举个具体的代码示例:
错误写法(会触发报错)
// 错误:用ExpectExec监听带RETURNING的INSERT mock.ExpectExec(`INSERT INTO "groups"`). WithArgs("2018-05-01 17:46:15", "2018-05-01 17:46:15", nil, "Group 1", "A good group"). WillReturnResult(sqlmock.NewResult(1, 1))
正确写法
// 正确:用ExpectQuery匹配完整的INSERT+RETURNING语句 mock.ExpectQuery(`INSERT INTO "groups" ("created_at","updated_at","deleted_at","name","description") VALUES ($1,$2,$3,$4,$5) RETURNING "groups"."id"`). WithArgs("2018-05-01 17:46:15", "2018-05-01 17:46:15", nil, "Group 1", "A good group"). // 模拟返回插入后的自增ID WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
额外注意点
- 可以用正则模糊匹配SQL:比如
^INSERT INTO "groups".*RETURNING "groups"."id",避免因为字段顺序或格式细节导致匹配失败 - 参数类型要完全对应:时间字符串的格式、nil值的位置,都要和gorm实际传入的参数一致
- 如果是MySQL数据库,gorm不会用RETURNING,而是依赖
LastInsertId,这时候才需要用ExpectExec+WillReturnResult的写法
这样调整后,gorm的Create操作就能正常通过sqlmock的模拟测试了!
内容的提问来源于stack exchange,提问作者Tom Mertz
相关产品推荐
相关产品推荐

