使用DATA-DOG/go-sqlmock测试含ID插入时ExpectExec失效问题排查
问题
使用DATA-DOG/go-sqlmock测试仓库的插入方法时,发现不包含ID字段的实体插入测试正常,但测试含ID的实体插入时,会收到“call to Query ... was not expected”错误。
工作代码示例:
type Some struct { SomeField string } some := Some{ SomeField: "123", } sqlMock.ExpectExec(regexp.QuoteMeta(`INSERT INTO "some"`)). WithArgs(some.SomeField). WillReturnResult(sqlmock.NewResult(1, 1)) err := repository.Insert(&some)
不工作代码示例:
type Some struct { ID string SomeField string } some := Some{ ID: "12345", SomeField: "123", } sqlMock.ExpectExec(regexp.QuoteMeta(`INSERT INTO "some"`)). WithArgs(some.ID, some.SomeField). WillReturnResult(sqlmock.NewResult(1, 1)) err := repository.Insert(&some)
其中repository.Insert封装了gorm.Create(some),请问忽略了什么?
解答
问题根源是GORM的默认行为和你设置的sqlmock预期不匹配:
- 当结构体包含
ID字段且已赋值时,GORM执行Create前会自动发起一条SELECT查询(格式类似SELECT * FROM "some" WHERE "some"."id" = ?),用来验证该ID是否已存在于数据库中。你当前只设置了对INSERT语句的ExpectExec,没有预期这条额外的查询,所以sqlmock抛出了未预期的Query错误。
解决方法
方法一:添加对查询语句的预期
在测试代码中,先为GORM的检查查询添加预期,返回空行表示该ID不存在,让GORM继续执行插入操作:
// 先预期检查ID的SELECT查询 sqlMock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "some" WHERE "some"."id" = ?`)). WithArgs(some.ID). WillReturnRows(sqlmock.NewRows([]string{"id", "some_field"})) // 返回空结果集 // 再预期INSERT语句 sqlMock.ExpectExec(regexp.QuoteMeta(`INSERT INTO "some"`)). WithArgs(some.ID, some.SomeField). WillReturnResult(sqlmock.NewResult(1, 1)) err := repository.Insert(&some)
方法二:修改GORM调用跳过ID检查
如果你不需要GORM自动检查ID唯一性,可以通过GORM的会话选项跳过默认的检查逻辑,比如在repository.Insert中修改为:
func (r *Repository) Insert(some *Some) error { // 使用SkipHooks跳过触发检查的前置钩子 return r.db.Session(&gorm.Session{SkipHooks: true}).Create(some).Error }
这种方式下,GORM不会执行前置的SELECT查询,你原来的sqlmock预期就可以正常工作。
内容的提问来源于stack exchange,提问作者freethinker
相关产品推荐
相关产品推荐

