英文:
Gorm delete with clauses sqlmock test
问题
我有一个带有返回结果的 Gorm 删除操作:
expirationDate := time.Now().UTC().Add(-(48 * time.Hour))
var deletedUsers Users
res := gormDB.WithContext(ctx).
Table("my_users").
Clauses(clause.Returning{Columns: []clause.Column{{Name: "email"}}}).
Where("created_at < ?", expirationDate).
Delete(&deletedUsers)
现在使用 clauses 的测试总是失败。例如:
sqlMock.ExpectExec(`DELETE`)
.WithArgs(expirationDate)
.WillReturnResult(sqlmock.NewResult(1, 1))
收到错误信息:
"call to Query 'DELETE FROM "my_users" WHERE created_at < $1 RETURNING "email"' with args [{Name: Ordinal:1 Value:2023-01-18 06:15:34.694274 +0000 UTC}], was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:
- matches sql: 'DELETE'
- is with arguments:
0 - 2023-01-18 06:15:34.694274 +0000 UTC - should return Result having:
LastInsertId: 1
RowsAffected: 1"
我尝试了许多其他的 sqlMock 期望,但它们都有类似的问题。
另外,在 ExpectExec 中我们没有返回值,只有在 ExpectQuery 中有返回值...
有没有人有办法测试带有 Clauses 的 Gorm 查询?
英文:
I have a Gorm delete with the returning result:
expirationDate := time.Now().UTC().Add(-(48 * time.hour))
var deletedUsers Users
res := gormDB.WithContext(ctx).
Table("my_users").
Clauses(clause.Returning{Columns: []clause.Column{{Name: "email"}}}).
Where("created_at < ?", expirationDate).
Delete(&deletedUsers)
Now the test with clauses always fails. e.g. :
sqlMock.ExpectExec(`DELETE`)
.WithArgs(expirationDate)
.WillReturnResult(sqlmock.NewResult(1, 1))
Receiving error:
"call to Query 'DELETE FROM "my_users" WHERE created_at < $1 RETURNING "email"' with args [{Name: Ordinal:1 Value:2023-01-18 06:15:34.694274 +0000 UTC}], was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:\n - matches sql: 'DELETE'\n - is with arguments:\n 0 - 2023-01-18 06:15:34.694274 +0000 UTC\n - should return Result having:\n LastInsertId: 1\n RowsAffected: 1"
I tried many other sqlMock expectations, but they have a similar issue.
Also, we don't have a return value in ExpectExec, only in ExpectQuery...
Any chance someone has to test the Gorm query with the Clauses?
答案1
得分: 1
我已经成功处理了你所需的内容。首先,让我分享我写的文件,然后我会逐步介绍所有相关的更改。文件repo.go
用于生产环境,repo_test.go
用于测试代码。
repo.go
package gormdelete
import (
"context"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type Users struct {
Email string
}
func Delete(ctx context.Context, gormDB *gorm.DB) error {
expirationDate := time.Now().UTC().Add(-(48 * time.Hour))
var deletedUsers Users
res := gormDB.WithContext(ctx).Table("my_users").Clauses(clause.Returning{Columns: []clause.Column{{Name: "email"}}}).Where("created_at < ?", expirationDate).Delete(&deletedUsers)
if res.Error != nil {
return res.Error
}
return nil
}
由于你没有提供完整的文件,我尽量猜测了缺失的部分。
repo_test.go
package gormdelete
import (
"context"
"database/sql/driver"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
// 这段代码直接从文档中复制而来
// https://github.com/DATA-DOG/go-sqlmock#matching-arguments-like-timetime
type AnyTime struct{}
// Match 满足 sqlmock.Argument 接口
func (a AnyTime) Match(v driver.Value) bool {
_, ok := v.(time.Time)
return ok
}
func TestDelete(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("不应该出现错误: %v", err)
}
conn, _ := db.Conn(context.Background())
gormDb, err := gorm.Open(postgres.New(postgres.Config{
Conn: conn,
}))
row := sqlmock.NewRows([]string{"email"}).AddRow("test@example.com")
mock.ExpectBegin()
mock.ExpectQuery("DELETE FROM \"my_users\" WHERE created_at < ?").WithArgs(AnyTime{}).WillReturnRows(row)
mock.ExpectCommit()
err = Delete(context.Background(), gormDb)
assert.Nil(t, err)
if err = mock.ExpectationsWereMet(); err != nil {
t.Errorf("未满足所有期望: %v", err)
}
}
这里还有一些值得注意的更改:
- 我按照文档实例化了
AnyTime
(你可以在注释中看到链接)。 - 再次,我猜测了
db
、mock
和gormDb
的设置,但我认为它们应该差不多。 - 我将
ExpectExec
的使用方式改为ExpectQuery
,因为我们将得到一个结果集,这是根据你的repo.go
文件中的Clauses
方法指定的。 - 你需要在
ExpectQuery
之前加上ExpectBegin
和ExpectCommit
。 - 最后,注意驱动程序在SQL语句中参数的期望方式的差异。在生产代码中,你可以选择使用
?
或$1
。然而,在测试代码中,你只能使用?
,否则它将无法匹配期望。
希望能有所帮助,如果还有其他问题,请告诉我!
英文:
I was able to successfully manage what you need. First, let me share the files I wrote, and then I'll walk you through all of the relevant changes. The files are repo.go
for production and repo_test.go
for the test code.
repo.go
package gormdelete
import (
"context"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type Users struct {
Email string
}
func Delete(ctx context.Context, gormDB *gorm.DB) error {
expirationDate := time.Now().UTC().Add(-(48 * time.Hour))
var deletedUsers Users
res := gormDB.WithContext(ctx).Table("my_users").Clauses(clause.Returning{Columns: []clause.Column{{Name: "email"}}}).Where("created_at < ?", expirationDate).Delete(&deletedUsers)
if res.Error != nil {
return res.Error
}
return nil
}
As you didn't provide the full file I tried to guess what was missing.
repo_test.go
package gormdelete
import (
"context"
"database/sql/driver"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
// this is taken directly from the docs
// https://github.com/DATA-DOG/go-sqlmock#matching-arguments-like-timetime
type AnyTime struct{}
// Match satisfies sqlmock.Argument interface
func (a AnyTime) Match(v driver.Value) bool {
_, ok := v.(time.Time)
return ok
}
func TestDelete(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error was not expected: %v", err)
}
conn, _ := db.Conn(context.Background())
gormDb, err := gorm.Open(postgres.New(postgres.Config{
Conn: conn,
}))
row := sqlmock.NewRows([]string{"email"}).AddRow("test@example.com")
mock.ExpectBegin()
mock.ExpectQuery("DELETE FROM \"my_users\" WHERE created_at < ?").WithArgs(AnyTime{}).WillReturnRows(row)
mock.ExpectCommit()
err = Delete(context.Background(), gormDb)
assert.Nil(t, err)
if err = mock.ExpectationsWereMet(); err != nil {
t.Errorf("not all expectations were met: %v", err)
}
}
Here, there are more changes that it's worth mentioning:
- I instantiated the
AnyTime
as per the documentation (you can see the link in the comment). - Again, I guessed the setup of the
db
,mock
, andgormDb
but I think it should be more or less the same. - I switch the usage of
ExpectExec
toExpectQuery
as we'll have back a result set as specified by theClauses
method in yourrepo.go
file. - You've to wrap the
ExpectQuery
within anExpectBegin
and anExpectCommit
. - Finally, pay attention to the difference in how the driver expects the parameters in the SQL statement. In the production code, you can choose to use
?
or$1
. However, in the test code, you can only use the?
otherwise it won't match the expectations.
Hope to help a little bit, otherwise, let me know!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论