一。介绍
gomock是golang官方开发维护的接口级别的mock方案,包含了GoMock包和mockgen工具两部分,其中GoMock包完成对桩对象生命周期的管理,mockgen工具用来生成interface对应的Mock类源文件。要使用gomock的一个前提是模块之间务必通过接口进行依赖,而不是依赖具体实现,否则mock会非常困难。这个工具目前业界用的并不多,主要是局限性太大,所以我们只需要简单了解一下如何使用就行。
github地址:https://github.com/golang/mock
二。使用
1. 安装
代码语言:txt复制go get -u github.com/golang/mock/gomock
go install github.com/golang/mock/mockgen
2. Quick start
第一步:定义接口,注意这个工具之支持对接口生成mock,其他的类型方法不支持。
代码语言:txt复制package dao
import (
"fmt"
_ "github.com/golang/mock/mockgen/model"
)
// 用户持久层操作对象
type UserDao interface {
Update(id, name, phoneNumber string) int64
Update2(param ...string) int64
Add(name, phoneNumber string) (int64, error)
Select(id string) int64
Delete(id string) int64
}
type userDao struct{}
func (u userDao) Update(id, name, phoneNumber string) int64 {
fmt.Println(id, name, phoneNumber)
return 1
}
第二步:使用命令生成mock类
代码语言:txt复制mockgen -destination mock_user_dao.go -package dao -source user_dao.go
如果觉得命令太长了,可以直接在UserDao上面加个go:generate
的注释如下
package dao
import (
"fmt"
_ "github.com/golang/mock/mockgen/model"
)
//go:generate mockgen -destination mock_user_dao.go -package dao -source user_dao.go
// 用户持久层操作对象
type UserDao interface {
Update(id, name, phoneNumber string) int64
Update2(param ...string) int64
Add(name, phoneNumber string) (int64, error)
Select(id string) int64
Delete(id string) int64
}
...
然后在当前包的目录下bash执行go generate
也是可以生成mock文件的。
第三步:自定义mock实现并进行断言
代码语言:txt复制package dao
import (
"errors"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"testing"
)
/**
* @Description
* @Author guirongguo
* @Email
* @Date 2021/8/31 20:37
**/
func Test_userDao(t *testing.T) {
// step1. 初始化一个mock controller
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
mockObj := NewMockUserDao(mockCtrl)
// step2. 定义mock的具体执行逻辑
// 1.参数支持Eq,Any,Not,Nil
//Eq(value) 表示与 value 等价的值。
//Any() 可以用来表示任意的入参。
//Not(value) 用来表示非 value 以外的值。
//Nil() 表示 None 值
//
// 2.mock方法调用次数支持如下
//Times() 断言 Mock 方法被调用的次数。
//MaxTimes() 最大次数。
//MinTimes() 最小次数。
//AnyTimes() 任意次数(包括 0 次)
//
// 3.返回值支持
// Return 返回确定的值
// Do Mock 方法被调用时,要执行的操作吗,忽略返回值。
// DoAndReturn 可以动态地控制返回值。
c1 := mockObj.EXPECT().Update("001", "ggr", "10010").Return(int64(1))
c2 := mockObj.EXPECT().Update("001", "ggr", gomock.Any()).Return(int64(1))
mockObj.EXPECT().Add("", "1001").Return(int64(0), errors.New("name is invalid")).AnyTimes()
mockObj.EXPECT().Add("1001", gomock.Not("1001")).Return(int64(0), errors.New("phone_number is invalid")).MinTimes(1)
mockObj.EXPECT().Add(gomock.Eq("1001"), "1001").Return(int64(0), errors.New("name is empty")).Times(1)
mockObj.EXPECT().Select(gomock.Any()).Return(int64(1)).MaxTimes(2)
mockObj.EXPECT().Update2(gomock.Any()).Return(int64(1)).Times(2)
mockObj.EXPECT().Delete("1001").Do(func(i string) {
t.Log("delete id=1001")
})
mockObj.EXPECT().Delete(gomock.Not("1001")).DoAndReturn(func(i string) int64 {
return int64(1)
})
// step3. 限制stub函数的调用顺序,如果顺序不一致,则报错
gomock.InOrder(c1, c2)
// step4. 测试验证
ret1 := mockObj.Update("001", "ggr", "10010")
ret2 := mockObj.Update("001", "ggr", "10020")
ret3 := mockObj.Update2("1", "")
ret4 := mockObj.Update2("")
ret5 := mockObj.Select("")
ret6, err1 := mockObj.Add("1001", "1001")
ret7, err2 := mockObj.Add("1001", "1002")
ret8 := mockObj.Delete("1001")
ret9 := mockObj.Delete("1002")
// step5. 断言
assert.Equal(t, ret1, int64(1))
assert.Equal(t, ret2, int64(1))
assert.Equal(t, ret3, int64(1))
assert.Equal(t, ret4, int64(1))
assert.Equal(t, ret5, int64(1))
assert.Equal(t, ret6, int64(0))
assert.Equal(t, ret7, int64(0))
assert.Equal(t, ret8, int64(0))
assert.Equal(t, ret9, int64(1))
assert.NotNil(t, err1)
assert.NotEmpty(t, err2)
}
自定义mock实现主要包含了自定义参数,自定义返回值,自定义mock调用次数以及调用顺序。
(1)自定义参数
参数支持Eq,Any,Not,Nil,分别代表一下含义:
- Eq(value) 用于参数为固定值的场景。
- Any() 用于任意参数的场景。
- Not(value) 用于表示参数非 value 以外的值场景。
- Nil() 用于表示参数None 值的场景
(2)自定义返回值
返回值支持如下几种:
- Return 用于返回确定的值的场景
- Do 用于无返回值的场景。
- DoAndReturn 用于可以动态地控制返回值。
(3)自定义mock调用次数
mock调用次数支持如下几种场景:
- Times() 断言 Mock 方法被调用的次数, 指定次数。
- MaxTimes() 最大次数。
- MinTimes() 最小次数。
- AnyTimes() 任意次数(包括 0 次)
(4)自定义mock调用顺序
当存在多个mock之间相互调用的情况时,可以通过一下2种方式定义mock执行的顺序:
- 直接在函数后面接
After
- 使用
gomock.InOrder
设置执行顺序
更多使用方法请参考官方文档:https://pkg.go.dev/github.com/golang/mock/gomock#pkg-examples