英文:
`could not match actual sql` error while mocking gorm `updates` using go-sqlmock?
问题
repository.go
func (repo *Repository) Update(info *model.Requests) error {
if info == nil {
return nil
}
columnChanges := map[string]interface{}{
status: "completed",
}
if err := repo.db.Table("requests").Model(info).Where(fmt.Sprintf("%s = ? AND %s = ? AND %s = ? AND %s = ?",
requestID, systemID, requestType, status), info.RequestID, info.SystemID, info.RequestType, "progress").Updates(columnChanges).Error; err != nil {
return err
}
return nil
}
Mock
repository_test.go
func TestRepository_Update(t *testing.T) {
type testData struct {
input *model.Requests
queryString string
queryArgs []driver.Value
updateErr error
hasErr bool
}
db, mock, _ := sqlmock.New()
defer db.Close()
dbInstance, _ := gorm.Open("postgres", db)
testDataList := []testData{
{
input: &model.Requests{
RequestID: 4,
SystemID: 2,
RequestType: "mobile",
Status: "completed",
},
queryString: `UPDATE "requests" SET "status" = $1 WHERE ("request_id" = $2 AND "system_id" = $3 AND "request_type" = $4 AND "status" = $5) `,
queryArgs: []driver.Value{"completed", 2, 4, "mobile", "progress"},
updateErr: nil,
hasErr: false,
},
}
for _, data := range testDataList {
repo := Repository(zerolog.Nop(), dbInstance)
if data.queryString != "" {
mock.ExpectBegin()
m := mock.ExpectExec(data.queryString).WithArgs(data.queryArgs...)
if data.hasErr {
m.WillReturnError(data.updateErr)
} else {
m.WillReturnResult(sqlmock.NewResult(1, 1))
}
mock.ExpectCommit()
}
resultErr := repo.Requests(data.input)
if data.hasErr {
assert.NotNil(t, resultErr)
} else {
assert.Nil(t, resultErr) //Error thrown in this line
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}
}
我得到的错误是
ExecQuery: could not match actual sql: "UPDATE "requests" SET "status" = $1 WHERE (request_id = $2 AND system_id = $3 AND request_type = $4 AND status = $5)" with expected regexp "UPDATE "requests" SET "status" = $1 WHERE ("request_id" = $2 AND "system_id" = $3 AND "request_type" = $4 AND "status" = $5)"
Error Trace: repository_test.go:<line number>
Error: Expected nil, but got: &errors.errorString{s:"ExecQuery: could not match actual sql: \"UPDATE \"requests\" SET \"status\" = $1 WHERE (request_id = $2 AND system_id = $3 AND request_type = $4 AND status = $5)\" with expected regexp \"UPDATE \"requests\" SET \"status\" = $1 WHERE (\"request_id\" = $2 AND \"system_id\" = $3 AND \"request_type\" = $4 AND \"status\" = $5)\""}
Test: Repository_Update
repository_test.go:<lineNumber>: there were unfulfilled expectations: there is a remaining expectation which was not matched: ExpectedExec => expecting Exec or ExecContext which:
- matches sql: 'UPDATE "requests" SET "status" = $1 WHERE ("request_id" = $2 AND "system_id" = $3 AND "request_type" = $4 AND "status" = $5) '
- is with arguments:
0 - success
1 - 2
2 - 4
3 - image
4 - pending
- should return Result having:
LastInsertId: 1
RowsAffected: 1
当我将gorm日志级别设置为true时,我看到的实际SQL是
UPDATE "requests" SET "status" = 'completed' WHERE (request_id = 5 AND system_id = 1 AND request_type = 'mobile' AND status = 'progress')
我尝试了将ExpectExec
更改为ExpectPrepare().ExpectExec
或mock.ExpectQuery(regexp.QuoteMeta
以及其他在go-sqlmock问题中提到的选项。但是没有一个起作用。我被困在这里已经两天了。请帮忙。
英文:
repository.go
func (repo *Repository) Update(info *model.Requests) error{
if info == nil{
return nil
}
columnChanges := map[string]interface{}{
status: “completed",
}
if err := repo.db.Table(“requests").Model(info).Where(fmt.Sprintf("%s = ? AND %s = ? AND %s = ? AND %s = ?",
requestID, systemID, requestType, status),info.RequestID, info.SystemID, info.RequestType,”progress").Updates(columnChanges).Error; err != nil {
return err
}
return nil
}
Mock
repository_test.go
func TestRepository_Update(t *testing.T) {
type testData struct {
input *model.Requests
queryString string
queryArgs []driver.Value
updateErr error
hasErr bool
}
db, mock, _ := sqlmock.New()
defer db.Close()
dbInstance, _ := gorm.Open("postgres", db)
testDataList := []testData{
{
input: &model.Requests{
RequestID: 4,
SystemID: 2,
RequestType: “mobile",
Status: “completed",
},
queryString: `UPDATE "requests" SET "status" = $1 WHERE (“request_id" = $2 AND “system_id" = $3 AND “request_type" = $4 AND "status" = $5) `,
queryArgs: []driver.Value{“completed", 2, 4, “mobile", “progress"},
updateErr: nil,
hasErr: false,
},
}
for _, data := range testDataList {
repo := Repository(zerolog.Nop(), dbInstance)
if data.queryString != "" {
mock.ExpectBegin()
m := mock.ExpectExec(data.queryString).WithArgs(data.queryArgs...)
if data.hasErr {
m.WillReturnError(data.updateErr)
} else {
m.WillReturnResult(sqlmock.NewResult(1, 1))
}
mock.ExpectCommit()
}
resultErr := repo.Requests(data.input)
if data.hasErr {
assert.NotNil(t, resultErr)
} else {
assert.Nil(t, resultErr) //Error thrown in this line
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}
}
Error I’m getting
ExecQuery: could not match actual sql: "UPDATE "requests" SET "status" = $1 WHERE (request_id = $2 AND system_id = $3 AND request_type = $4 AND status = $5)" with expected regexp "UPDATE "requests" SET "status" = $1 WHERE ("request_id" = $2 AND "system_id" = $3 AND "request_type" = $4 AND "status" = $5)”
Error Trace: repository_test.go:<line number>
Error: Expected nil, but got: &errors.errorString{s:"ExecQuery: could not match actual sql: \"UPDATE \"requests\" SET \"status\" = $1 WHERE (request_id = $2 AND system_id = $3 AND request_type = $4 AND status = $5)\" with expected regexp \"UPDATE \"requests\" SET \"status\" = $1 WHERE (\”request_id\" = $2 AND \”system_id\" = $3 AND \”request_type\" = $4 AND \"status\" = $5)\""}
Test: Repository_Update
repository_test.go:<lineNumber>: there were unfulfilled expectations: there is a remaining expectation which was not matched: ExpectedExec => expecting Exec or ExecContext which:
- matches sql: 'UPDATE "requests" SET "status" = $1 WHERE (“request_id" = $2 AND “system_id" = $3 AND “request_type" = $4 AND "status" = $5) '
- is with arguments:
0 - success
1 - 2
2 - 4
3 - image
4 - pending
- should return Result having:
LastInsertId: 1
RowsAffected: 1
When I set gorm log level true, this is the actual SQL I see
UPDATE "requests" SET "status" = ‘completed' WHERE (request_id = 5 AND system_id = 1 AND request_type = ‘mobile' AND status = ‘progress')
I tried, changing ExpectExec
to ExpectPrepare().ExpectExec
or mock.ExpectQuery(regexp.QuoteMeta
and other options mentioned in go-sqlmock issues by other tickets. None of them worked. Struck with this for 2 days. Please help.
答案1
得分: 6
原因是你没有转义查询字符串的正则表达式。Sqlmock期望在你的期望中使用正则表达式。
如果你想匹配完整的查询字符串,而不是其中的一部分,你可以指定一个sqlmock模拟选项,像这样:
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
答案在文档中,如果你能阅读一次API文档,这可以节省你的时间。
英文:
the reason is that you are not escaping your regular expression of query string. Sqlmock expects regular expression in your expectation.
Now in case if you want to match exact query string, not parts of it.
You can specify an sqlmock mock option, like this:
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
The answer was in the documentation, if you would read the api docs once, this could save your time.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论