首页  >  文章  >  后端开发  >  去模拟

去模拟

WBOY
WBOY原创
2024-07-31 09:16:32736浏览

Go mock

什么是 Go 模拟

Go mock 是一个模拟框架,允许开发人员为我们的接口创建模拟结构并定义其行为。

例如,它可以帮助我们生成 Reponsitory 的模拟实现,并根据某些输入定义预期输出。

原始仓库:https://github.com/golang/mock

现在它不再维护了,所以我们应该使用 https://github.com/uber-go/mock 来代替。

安装

go install go.uber.org/mock/mockgen@latest

主要特点

  • 模拟生成:gomock 包含一个名为mockgen 的工具,可以自动生成接口的模拟实现。
  • 灵活的期望:使用 gomock,您可以对模拟对象的行为定义精确的期望,例如:
    • 它应该接收的参数
    • 以及它应该返回的值
    • 应调用方法的具体次数、最小或最大次数

Mockgen命令

按照本文档了解如何使用mockgen CLI:

https://github.com/uber-go/mock?tab=readme-ov-file#running-mockgen

用法

假设你有一个 IUserRepo 接口,其中包含一些方法:

ports.go

package user

type IUserRepo interface {
    GetUserByID(id int) (*User, error)
    Insert(user User) error
    Update(id int, user User) error
}

domain.go

package user

type User struct {
    ID   int
    Name string
}

service.go

package user

import "fmt"

type UserService struct {
    repo IUserRepo
}

var invalidUserIDError = fmt.Errorf("invalid user id")

func (u *UserService) Upsert(user User) error {
    if user.ID <= 0 {
        return invalidUserIDError
    }
    existingUser, err := u.repo.GetUserByID(user.ID)
    if err != nil {
        return err
    }
    if existingUser == nil {
        return u.repo.Insert(user)
    }

    return u.repo.Update(user.ID, user)
}

1。运行mockgen生成模拟实例

go run go.uber.org/mock/mockgen@latest -source=interface.go -destination=mock.go -package=user
  • source:指定包含Repository接口的文件。
  • 目的地:指定将写入生成的模拟代码的文件。
  • package:指定生成的mock的包名。

2。指定期望

service_test.go

package user

import (
    "go.uber.org/mock/gomock"
    "github.com/stretchr/testify/assert"
    "testing"
)

func TestUpsertUser(t *testing.T) {
    mockCtl := gomock.NewController(t)
    defer mockCtl.Finish()

    tests := []struct {
        name                 string
        user                 User
        specifyFunctionCalls func(mock *MockIUserRepo)
        expectedError        error
    }{
        {
            user: User{ID: 1, Name: "User 1"},
            name: "Should insert",
            specifyFunctionCalls: func(mockRepo *MockIUserRepo) {
                mockRepo.EXPECT().GetUserByID(1).Return(nil, nil).Times(1)
                mockRepo.EXPECT().Insert(User{ID: 1, Name: "User 1"}).Return(nil).Times(1)
            },
        },
        {
            name: "User existed - Should update",
            user: User{ID: 1, Name: "New User Name"},
            specifyFunctionCalls: func(mockRepo *MockIUserRepo) {
                mockRepo.EXPECT().GetUserByID(1).Return(&User{ID: 1, Name: "User 1"}, nil).Times(1)
                mockRepo.EXPECT().Update(1, User{ID: 1, Name: "New User Name"}).Return(nil).Times(1)
            },
        },
        {
            expectedError: invalidUserIDError,
        },
    }

    for _, test := range tests {
        t.Run(test.name, func(t *testing.T) {
            mockRepo := NewMockIUserRepo(mockCtl)
            if test.specifyFunctionCalls != nil {
                test.specifyFunctionCalls(mockRepo)
            }
            userService := UserService{repo: mockRepo}
            err := userService.Upsert(test.user)
            assert.Equal(t, test.expectedError, err)
        })
    }
}

在上面的测试文件中:

  • specifyFunctionCalls 允许我们自定义模拟函数期望,必须调用哪些函数以及必须调用这些函数多少次。
  • 如果您尝试在specifyFunctionCalls规范中添加冗余函数(例如在第一个测试中添加mockRepo.EXPECT().Update(....)),您的测试将因错误而失败:missing call(s) .
  • 如果您的服务调用尚未指定的函数,您的测试将因错误而失败:没有预期调用该接收者的“Insert”方法。

源代码

https://github.com/huantt/gomock-demo

以上是去模拟的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
上一篇:Unit Test At Go下一篇:Sentenças aninhadas