使用 gorm 和 sqlmock 进行测试:无法将查询与预期的正则表达式匹配

问题描述 投票:0回答:1

我正在使用 GORM 开发一个项目,并尝试使用 SQLMock 编写测试。下面是使用 GORM 定义自定义类型和数据库模型的代码片段:

// ActionType action type
type ActionType int

const (
    // ActionTypeFixedPayment fixed payment action type
    ActionTypeFixedPayment ActionType = 1
    // ActionTypePercentileCommissionExemption percentile commission exemption action type
    ActionTypePercentileCommissionExemption ActionType = 2
)

var ActionTypeValues = map[ActionType]string{
    ActionTypeFixedPayment:                  "FixedPayment",
    ActionTypePercentileCommissionExemption: "Commission",
}

func (a ActionType) String() string {
    return ActionTypeValues[a]
}

// IncentiveAction incentive action
type IncentiveAction struct {
    database.Model
    IncentiveDateID uint           `json:"incentive_date_id" gorm:"column:incentive_date_id;not null;index"`
    DriverID        uint           `json:"driver_id" gorm:"column:driver_id;not null;index"`
    StateID         uint           `json:"state_id" gorm:"column:state_id;not null;index"`
    Type            ActionType     `json:"type" gorm:"column:type;not null;index"`
    Value           string         `json:"value" gorm:"column:value;type:VARCHAR"`
    Meta            datatypes.JSON `json:"meta" gorm:"column:meta;type:jsonb"`
}

func (m *IncentiveAction) TableName() string {
    return "incentive_actions"
}

我编写了一个简单的函数,使用以下代码将数据插入数据库:

type actionRepo struct {
    dbRead,
    dbWrite *gorm.DB
}

func (sr *actionRepo) Create(ctx context.Context, data models.IncentiveAction) (
    *models.IncentiveAction, error) {

    err := sr.dbWrite.WithContext(ctx).Create(&data).Error
    if err != nil {
        fmt.Errorf("%s", err.Error())
    }

    return &data, err
}

我想为这个函数编写一个测试,所以我使用 gorm 和 sqlmock 创建了一个模拟数据库:

// MockDB get fresh mocked db instance
func MockDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock, func() error) {
    db, mock, err := sqlmock.New()
    if err != nil {
        t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
    }

    pgDialect := postgres.New(postgres.Config{Conn: db})

    gdb, err := gorm.Open(pgDialect, &gorm.Config{
        SkipDefaultTransaction: true,
    })
    if err != nil {
        t.Fatalf("error when opening database connection: %s", err)
    }

    return gdb, mock, db.Close
}

最后我正在编写测试以在数据库中创建一条记录,稍后我想使用以下代码检查结果:

func TestActionRepo_Create(t *testing.T) {
    assertions := assert.New(t)
    db, dbMock, closeDb := MockDB(t)
    defer closeDb()

    repo := NewActionRepo(db, db)
    ctx := context.Background()

    actionToCreate := models.IncentiveAction{
        IncentiveDateID: 1,
        DriverID:        2,
        StateID:         3,
        Type:            models.ActionTypeFixedPayment,
        Value:           "5000",
        Meta:            nil,
    }

    actionRows := sqlmock.NewRows([]string{"id"})
    actionRows.AddRow(uint(1))

    actionQuery := `INSERT INTO "incentive_actions" ("created_at","updated_at","incentive_date_id","driver_id","state_id","type","value","meta") VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING "id"`

    dbMock.ExpectQuery(regexp.QuoteMeta(actionQuery)).
        WithArgs(sqlmock.AnyArg(), sqlmock.AnyArg(), 1, 2, 3, models.ActionTypeFixedPayment, "5000", nil).
        WillReturnRows(actionRows)

    action, err := repo.Create(ctx, actionToCreate)

    assertions.NoError(err)
    assertions.Equal(uint(1), action.ID)
}

运行此代码会导致错误:

Query: could not match actual sql: "INSERT INTO "incentive_actions" ("created_at","updated_at","incentive_date_id","driver_id","state_id","type","value","meta") VALUES ($1,$2,$3,$4,$5,$6,$7,NULL) RETURNING "id"" with expected regexp "INSERT INTO "incentive_actions" \("created_at","updated_at","incentive_date_id","driver_id","state_id","type","value","meta"\) VALUES \(\$1,\$2,\$3,\$4,\$5,\$6,\$7,\$8\) RETURNING "id""
            Test:           TestActionRepo_Create

Error:          Not equal:
                            expected: 0x1
                            actual  : 0x0
            Test:           TestActionRepo_Create

我怀疑这个问题可能与 GORM 和 SQLMock 如何处理元字段有关,但我不确定如何解决它。我使用的是 GORM 版本 1.25.10 和 SQLMock 版本 1.5.2。此测试曾在 GORM 1.21 等早期版本中运行。

任何人都可以提供有关如何解决此问题的指导或对可能出现问题的任何见解吗?谢谢!

go go-gorm go-sqlmock
1个回答
0
投票

你是对的,它与“元”字段有关。似乎发生的情况是,当 GORM 构建表达式时,它遇到您为“meta”传递的 nil 值,并使用“NULL”构建 SQL 语句来代替最后一个可行的($8)。

这是因为元字段使用

datatypes.JSON
类型。当 GORM 调用其内部
AddVar()
方法时,在反射期间,它会调用
GormValue()
数据类型上的
JSON
方法,如果底层
json.RawMessage
字节切片长度为零,则返回“NULL”。为了让字符串匹配并通过测试,你可以用一些 json 测试数据填充元字段:

metaAttr := datatypes.JSON([]byte(`{"name":"Jackie Daytona"}`))
actionToCreate := IncentiveAction{
    IncentiveDateID: 1,
    DriverID:        2,
    StateID:         3,
    Type:            ActionTypeFixedPayment,
    Value:           "5000",
    Meta:            metaAttr,
}

actionRows := sqlmock.NewRows([]string{"id"})
actionRows.AddRow(uint(1))

actionQuery := `INSERT INTO "incentive_actions" ("created_at","updated_at","incentive_date_id","driver_id","state_id","type","value","meta") VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING "id"`
dbMock.ExpectQuery(regexp.QuoteMeta(actionQuery)).
    WithArgs(sqlmock.AnyArg(), sqlmock.AnyArg(), 1, 2, 3, ActionTypeFixedPayment, "5000", metaAttr).
    WillReturnRows(actionRows)
© www.soinside.com 2019 - 2024. All rights reserved.