sqlmock单元测试sqlx批量插入功能报意外错误该如何解决
问题根源
你代码中调用的sqlx.Named()方法会在运行时自动将带命名参数(:开头)的SQL语句转换为数据库驱动兼容的?占位符格式,同时把结构体参数解析为有序参数列表。你的测试代码预期匹配的是原始带命名参数的SQL,但实际发送给sqlmock的是转换后的?占位符SQL,因此出现匹配失败。
另外你的业务代码中没有显式调用Prepare方法,直接执行了Exec操作,测试中添加的ExpectPrepare预期本身也不符合实际执行逻辑。
解决步骤
- 将测试中预期匹配的SQL替换为转换后的
?占位符版本 - 移除不必要的
ExpectPrepare逻辑,直接使用ExpectExec匹配执行的SQL - 可搭配正则匹配避免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
相关产品推荐
相关产品推荐

