在进行项目开发或者测试的时候,我们经常需要一些数据来进行测试。例如某个方法调用了数据库,如果我们真的去准备一个数据库并添加数据进行测试的话,未免太麻烦了些。而且我们只想测试一些业务逻辑,如果真的去连接数据库,会引入额外的逻辑,有时候会干扰我们测试。这时候怎么办呢,我们可以利用一些现有的库去mock一些数据供我们测试。
常用的Mocking工具
gomock
:这是Google提供的一个Go库,它支持结构化地定义mock对象,并且提供了丰富的断言功能。testify/mock
:这是一个非常流行的测试辅助库的一部分,提供了一种简洁的方式来创建和管理mock对象。minimock
:另一个轻量级的选择,适合于简单的mock需求。
示例:使用 gomock
创建一个简单的mock
步骤 1: 安装 testify 库
首先确保你的项目已经安装了gomock
库。可以通过运行以下命令来安装:
go get -u github.com/golang/mock
go get -u github.com/golang/mockgen
go install github.com/golang/mockgen
步骤 2: 定义接口
新建文件 mock
新建文件 user.go
假设你有一个服务需要调用数据库(这里简化为一个简单的查询函数):
package mock
import "context"
type User struct {
Nickname string
Mobile string
Password string
}
type UserService struct {
db UserData
}
func (us *UserService) GetUserByMobile(ctx context.Context, mobile string) (User, error) {
//需要测试的业务逻辑
user,err:= us.db.GetUserByMobile(ctx, mobile)
if err !=nil{
return User{},err
}
user.Mobile="18"
//
return user, nil
}
type UserData interface {
GetUserByMobile(ctx context.Context, mobile string) (User, error)
}
步骤 3: 使用Mockgen创建mock文件
进入mock文件夹下的目录
cd ./mock
执行mockgen
命令,指定源文件user.go
,生成目标文件user_mock.go
,指定包mock
mockgen -source user.go -destination user_mock.go -package mock
可以看到会自动生成一个user_mock.go
文件
创建文件夹user_test.go
,注意,一定要以_test结尾,这是Go的测试文件的约定。
编写测试代码:
package mock
import (
"context"
"github.com/golang/mock/gomock"
"testing"
)
func TestUserService_GetUserByMobile(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockUserData := NewMockUserData(ctrl)
mockUserData.EXPECT().GetUserByMobile(gomock.Any(), "13812345678").Return(
User{Nickname: "zxq"}, nil,
)
userService := UserService{db: mockUserData}
user, err := userService.GetUserByMobile(context.Background(), "13812345678")
if err != nil {
t.Errorf("err should be nil, but got %v", err)
return
}
if user.Mobile != "18" {
t.Errorf("user.Nickname should be \"zxq\", but got %v", user.Nickname)
}
}
注意这段代码,我们在其中传入了参数,并指定返回什么结果:
mockUserData.EXPECT().GetUserByMobile(gomock.Any(), "13812345678").Return(
User{Nickname: "zxq"}, nil,
)
然后我们调用测试函数
user, err := userService.GetUserByMobile(context.Background(), "13812345678")
检查函数返回值是否正确:
if user.Mobile != "18" {
t.Errorf("user.Nickname should be \"zxq\", but got %v", user.Nickname)
}
我们通过mock指定了数据库会返回什么结果,排除了数据库的感应,我们测试的是我们的业务逻辑是否正确。
通过这个例子,我们知道了可使用mock的数据来指定某些调用返回的结果,从而排除了它的影响,避免测试业务逻辑的时候,要去把上线时的运行环境准备好之后才能测试。