首页 > 解决方案 > 带有gorm INSERT的sqlmock

问题描述

我在模拟 gorm INSERT 查询时遇到了很多麻烦。选择正常时我能够通过测试,但插入时遇到此错误。

# gorms's debug output
INSERT INTO "groups" ("created_at","updated_at","deleted_at","name","description") VALUES ('2018-05-01 17:46:15','2018-05-01 17:46:15',NULL,'Group 1','A good group') RETURNING "groups"."id"

# Error returned from *gorm.DB.Create
2018/05/01 17:46:15 Error creating group: call to Query 'INSERT INTO "groups" ("created_at","updated_at","deleted_at","name","description") VALUES ($1,$2,$3,$4,$5) RETURNING "groups"."id"' with args [{Name: Ordinal:1 Value:2018-05-01 17:46:15.384319544 -0700 PDT m=+0.005382104} {Name: Ordinal:2 Value:2018-05-01 17:46:15.384319544 -0700 PDT m=+0.005382104} {Name: Ordinal:3 Value:<nil>} {Name: Ordinal:4 Value:Group 1} {Name: Ordinal:5 Value:A good group}], was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:
  - matches sql: '^INSERT INTO "groups" (.+)$'
  - is with arguments:
    0 - {}
    1 - {}
    2 - <nil>
    3 - Group 1
    4 - A good group
  - should return Result having:
      LastInsertId: 1
      RowsAffected: 1

我尝试了多个不同版本的正则表达式,甚至在 regex101.com 上使用 golang 测试了匹配,但我似乎无法让我的 sqlmock 匹配 gorm 的插入。

这是我的测试:

type AnyTime struct{} // I don't actually know if I even need this

func (a AnyTime) Match(v driver.Value) bool {
    _, ok := v.(time.Time)
    return ok
}

func TestGroupService_Create(t *testing.T) {
    db, mock, err := sqlmock.New()
    if err != nil {
        log.Fatalf("can't create sqlmock: %s", err)
    }
    models.DB, err = gorm.Open("postgres", db)
    if err != nil {
        log.Fatalf("can't open gorm connection: %s", err)
    }
    defer db.Close()

    models.DB.LogMode(true)

    name := "Group 1"
    description := "A good group"

    mock.ExpectExec("^INSERT INTO \"groups\" (.+)$").WithArgs(AnyTime{}, AnyTime{}, nil, name, description).WillReturnResult(sqlmock.NewResult(1, 1))

    s := GroupService{}

    req := &pb.CreateGroupRequest{
        Name: name,
        Description: description,
    }

    resp, err := s.Create(context.Background(), req)
    assert.Nil(t, err)

    if assert.NotNil(t, resp) {
        assert.Equal(t, resp.Group.Name, name)
        assert.Equal(t, resp.Group.Description, description)
    }

    err = mock.ExpectationsWereMet()
    assert.Nil(t, err)
}

以及我正在尝试测试的服务方法:

func (server *GroupService) Create(ctx context.Context, request *pb.CreateGroupRequest) (*pb.CreateGroupReply, error) {
    var group models.Group

    group.Name = request.Name
    group.Description = request.Description

    db := models.DB

    if err := db.Create(&group).Error; err != nil {
        log.Printf("Error creating group: %v", err)
        return nil, err
    }

    createReply := pb.CreateGroupReply{
        Group: mapGroupToService(group),
    }

    return &createReply, nil
}

我似乎无法弄清楚这一点。谢谢!

标签: testingmockinggrpcgo-gorm

解决方案


You need to change mock.ExpectExec to mock.ExpectQuery.

It's a known issue for GORM with PostgreSQL.

For more explanation, check this article.


推荐阅读