sqlmock与gorm INSERT

时间:2018-05-03 21:57:31

标签: testing mocking grpc go-gorm

我在模拟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
}

我似乎无法解决这个问题。谢谢!

2 个答案:

答案 0 :(得分:0)

我可以通过切换到go-mocket库来模拟和插入。它似乎是专门为GORM制作的。我很想知道为什么sqlmock插入不起作用,但这是我现在的解决方案:

func SetupTests() *gorm.DB {
    mocket.Catcher.Register()

    db, err := gorm.Open(mocket.DRIVER_NAME, "")
    if err != nil {
        log.Fatalf("error mocking gorm: %s", err)
    }
    // Log mode shows the query gorm uses, so we can replicate and mock it
    db.LogMode(true)
    models.DB = db

    return db
}

func TestGroupService_Create(t *testing.T) {
    db := SetupTests()
    defer db.Close()

    var mockedId int64 = 64
    mocket.Catcher.Reset().NewMock().WithQuery("INSERT INTO \"groups\"").WithId(mockedId)

    s := GroupService{}

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

    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, uint32(mockedId), resp.Group.Id)
        assert.Equal(t, name, resp.Group.Name)
        assert.Equal(t, description, resp.Group.Description)
    }
}

答案 1 :(得分:0)

您需要将mock.ExpectExec更改为mock.ExpectQuery

对于PostgreSQL的GORM,这是众所周知的issue

有关更多说明,请选中此article