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

使用sqlmock测试GORM多INSERT操作时遇错误求排查

问题定位与解决方案

错误原因

你只定义了2个ExpectPrepare,但业务逻辑要执行3次INSERT操作,第三次调用时没有匹配的Prepare预期,直接触发sqlmock的未预期操作报错。另外,GORM调用Create方法时,内部会先执行PREPARE生成预编译SQL,再执行EXEC完成插入——每一次Create调用都会对应一套完整的PREPARE + EXEC流程,三次循环就需要三次对应的预期,或者配置允许复用Prepare对象。

解决方案1:复用Prepare对象(推荐)

使用sqlmock的WillReusePrepare()方法,让同一个Prepare可以被多次执行,只需要定义一次Prepare,再对应三次Exec预期即可:

func mockDatabase(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {
    db, mock, err := sqlmock.New()
    if err != nil {
        t.Fatalf("failed to open sqlmock database: %v", err)
    }
    gormDB, err := gorm.Open(mysql.New(mysql.Config{
        Conn: db,
        SkipInitializeWithVersion: true,
    }), &gorm.Config{})
    if err != nil {
        t.Fatalf("failed to open gorm database: %v", err)
    }

    // 定义一次Prepare,允许复用
    mock.ExpectPrepare(`^INSERT INTO \`TABLE\`(.+)$`).WillReusePrepare()
    // 添加三次Exec预期,参数替换为实际业务数据
    for i := 0; i < 3; i++ {
        mock.ExpectExec(`^INSERT INTO \`TABLE\`(.+)$`).
            WithArgs("param1_value", "param2_value").
            WillReturnResult(sqlmock.NewResult(1, 1))
    }

    return gormDB, mock
}

解决方案2:显式定义三次Prepare+Exec预期

如果不想复用Prepare,可以直接循环三次,每次都定义对应的Prepare和Exec:

func mockDatabase(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {
    db, mock, err := sqlmock.New()
    if err != nil {
        t.Fatalf("failed to open sqlmock database: %v", err)
    }
    gormDB, err := gorm.Open(mysql.New(mysql.Config{
        Conn: db,
        SkipInitializeWithVersion: true,
    }), &gorm.Config{})
    if err != nil {
        t.Fatalf("failed to open gorm database: %v", err)
    }

    // 循环三次,每次定义Prepare和对应的Exec
    for i := 0; i < 3; i++ {
        mock.ExpectPrepare(`^INSERT INTO \`TABLE\`(.+)$`)
        mock.ExpectExec(`^INSERT INTO \`TABLE\`(.+)$`).
            WithArgs("param1_value", "param2_value").
            WillReturnResult(sqlmock.NewResult(1, 1))
    }

    return gormDB, mock
}

额外注意事项

  • 确保WithArgs中的参数顺序和GORM生成的SQL参数顺序完全一致,GORM会按照结构体字段顺序(或tag指定的顺序)生成参数,顺序不匹配也会触发未预期错误。
  • 测试结束后务必调用mock.ExpectationsWereMet(),验证所有预期操作都被执行。

内容的提问来源于stack exchange,提问作者Carlos Benjumea

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 04:37:02