在使用go-sqlmock模拟gorm的`updates`时出现了`could not match actual sql`错误。

huangapple go评论105阅读模式
英文:

`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().ExpectExecmock.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&quot;,
}
if err := repo.db.Table(“requests&quot;).Model(info).Where(fmt.Sprintf(&quot;%s = ? AND  %s = ? AND %s = ? AND %s = ?&quot;,
requestID, systemID, requestType, status),info.RequestID, info.SystemID, info.RequestType,”progress&quot;).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(&quot;postgres&quot;, db)
testDataList := []testData{
{
input: &amp;model.Requests{
RequestID: 4,
SystemID: 2,
RequestType: “mobile&quot;,
Status: “completed&quot;,
},
queryString: `UPDATE &quot;requests&quot; SET &quot;status&quot; = $1 WHERE (“request_id&quot; = $2 AND “system_id&quot; = $3 AND “request_type&quot; = $4 AND &quot;status&quot; = $5) `,
queryArgs:   []driver.Value{“completed&quot;, 2, 4, “mobile&quot;, “progress&quot;},
updateErr:   nil,
hasErr: false,
},
}
for _, data := range testDataList {
repo := Repository(zerolog.Nop(), dbInstance)
if data.queryString != &quot;&quot; {
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(&quot;there were unfulfilled expectations: %s&quot;, err)
}
}
}

Error I’m getting

ExecQuery: could not match actual sql: &quot;UPDATE &quot;requests&quot; SET &quot;status&quot; = $1 WHERE (request_id = $2 AND system_id = $3 AND request_type = $4 AND status = $5)&quot; with expected regexp &quot;UPDATE &quot;requests&quot; SET &quot;status&quot; = $1 WHERE (&quot;request_id&quot; = $2 AND &quot;system_id&quot; = $3 AND &quot;request_type&quot; = $4 AND &quot;status&quot; = $5)”
Error Trace:    repository_test.go:&lt;line number&gt;
Error:          Expected nil, but got: &amp;errors.errorString{s:&quot;ExecQuery: could not match actual sql: \&quot;UPDATE \&quot;requests\&quot; SET \&quot;status\&quot; = $1 WHERE (request_id = $2 AND system_id = $3 AND request_type = $4 AND status = $5)\&quot; with expected regexp \&quot;UPDATE \&quot;requests\&quot; SET \&quot;status\&quot; = $1 WHERE (\”request_id\&quot; = $2 AND \”system_id\&quot; = $3 AND \”request_type\&quot; = $4 AND \&quot;status\&quot; = $5)\&quot;&quot;}
Test:           Repository_Update
repository_test.go:&lt;lineNumber&gt;: there were unfulfilled expectations: there is a remaining expectation which was not matched: ExpectedExec =&gt; expecting Exec or ExecContext which:
- matches sql: &#39;UPDATE &quot;requests&quot; SET &quot;status&quot; = $1 WHERE (“request_id&quot; = $2 AND “system_id&quot; = $3 AND “request_type&quot; = $4 AND &quot;status&quot; = $5) &#39;
- 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 &quot;requests&quot; SET &quot;status&quot; = ‘completed&#39;  WHERE (request_id = 5 AND  system_id = 1 AND request_type = ‘mobile&#39; AND status = ‘progress&#39;)

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.

huangapple
  • 本文由 发表于 2020年4月3日 20:01:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/61011456.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定