You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:30:44