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

sqlmock单元测试sqlx批量插入功能报意外错误该如何解决

问题根源

你代码中调用的sqlx.Named()方法会在运行时自动将带命名参数(:开头)的SQL语句转换为数据库驱动兼容的?占位符格式,同时把结构体参数解析为有序参数列表。你的测试代码预期匹配的是原始带命名参数的SQL,但实际发送给sqlmock的是转换后的?占位符SQL,因此出现匹配失败。
另外你的业务代码中没有显式调用Prepare方法,直接执行了Exec操作,测试中添加的ExpectPrepare预期本身也不符合实际执行逻辑。

解决步骤

  1. 将测试中预期匹配的SQL替换为转换后的?占位符版本
  2. 移除不必要的ExpectPrepare逻辑,直接使用ExpectExec匹配执行的SQL
  3. 可搭配正则匹配避免SQL格式差异导致的匹配失败

修改后的测试代码示例

var repo *SchoolRegistry

func TestAddStudentEntriesSuccess(t *testing.T) {
     db, mock, err := sqlmock.New()
     if err != nil {
            t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
     }
     sqlxDb := sqlx.NewDb(db, "sqlmock")
     defer sqlxDb.Close()

     repo = &SchoolRegistry{sqlxDb}
     if err := logger.Init(logLevel, logTimeFormat); err != nil {
         fmt.Printf("failed to initialize logger: %v", err)
     }

    // 改为匹配转换后的占位符SQL,使用正则转义避免格式差异
    expectedSQL := regexp.QuoteMeta(`insert into StudentEntries (registerId, studentId, fee, admissionDate) values (?, ?, ?, ?)`)
    args := []models.StudentDbEntry{
        {
            RegisterID:     1,
            StudentID:      1,
            Fee:            "100",
            AdmissionDate:  "10-20-2015",
        },
    }

    // 直接预期Exec操作,不需要Prepare
    mock.ExpectExec(expectedSQL).
        WithArgs(1,1,"100","10-20-2015").
        WillReturnResult(sqlmock.NewResult(0, 1)) // 插入成功影响行数为1,可根据实际场景调整

    err := repo.AddStudentEntries(helper.FetchTestContext(), args)
    assert.NoError(t, err)
    // 可选:验证所有预期都被正常执行
    assert.NoError(t, mock.ExpectationsWereMet())
}

批量插入场景注意事项

如果你的测试用例传入多个StudentDbEntry元素,sqlx.Named会自动生成多组占位符,例如2条数据会生成values (?, ?, ?, ?),(?, ?, ?, ?),此时需要对应调整预期SQL的占位符数量和参数列表。

内容的提问来源于stack exchange,提问作者Muhammed Roshan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 05:30:04