mock 是Go官方提供的测试框架,很好的集成testing包,实现对interface的mock。
安装mock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
> go version
go version go1.16.6 darwin/amd64
> go install github.com/golang/mock/mockgen@v1.6.0
go: downloading github.com/golang/mock v1.6.0
go: downloading golang.org/x/mod v0.4.2
go: downloading golang.org/x/tools v0.1.1
go: downloading golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
go: downloading golang.org/x/sys v0.0.0-20210510120138-977fb7262007
> mockgen -version
v1.6.0
> mockgen -help
|
mockgen支持选项:
- -source:包含要mock的接口的文件。
- -destination:要将生成的源代码写入其中的文件。如果未设置此值,则代码将打印到标准输出。
- -package:用于生成的模拟类源代码的包。如果不设置此项,则包名称mock_与输入文件的包连接。
- -import:应在生成的源代码中使用的显式导入列表,指定为 foo=bar/baz 形式的逗号分隔元素列表,其中 bar/baz 是要导入的包,foo 是生成的源代码中用于包的标识符。
- -aux_files: 应查阅的其他文件列表,以解决例如在不同文件中定义的嵌入式接口。这被指定为以逗号分隔的元素列表 foo=bar/baz.go,其中 bar/baz.go 是源文件,foo 是 -source 文件使用的该文件的包名
- -build_flags:(仅反射模式)标志逐字传递以进行构建
mockgen使用示例
mockgen
有两种操作模式:源文件和反射。
源文件模式使用-source
指定包含interface定义的文件,生成mock类文件。
1
|
mockgen -source=foo.go [other options]
|
反射模式没有标识参数,第一个参数是项目导入路径,第二个参数是interface名称(多个用逗号分隔)
1
|
mockgen database/sql/driver Conn,Driver
|
准备接口文件foo.go,接口内容如下:
1
2
3
4
5
6
7
8
9
10
|
package gomock
type Foo interface {
Bar(x int) int
}
func SUT(f Foo) int {
return f.Bar(99)
}
|
使用mockgen
生成mock文件,执行如下:
1
|
mockgen -source=./foo.go -destination=./mock_foo.go -package=gomock
|
生成的mock文件内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
// Code generated by MockGen. DO NOT EDIT.
// Source: ./foo.go
// Package gomock is a generated GoMock package.
package gomock
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockFoo is a mock of Foo interface.
type MockFoo struct {
ctrl *gomock.Controller
recorder *MockFooMockRecorder
}
// MockFooMockRecorder is the mock recorder for MockFoo.
type MockFooMockRecorder struct {
mock *MockFoo
}
// NewMockFoo creates a new mock instance.
func NewMockFoo(ctrl *gomock.Controller) *MockFoo {
mock := &MockFoo{ctrl: ctrl}
mock.recorder = &MockFooMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockFoo) EXPECT() *MockFooMockRecorder {
return m.recorder
}
// Bar mocks base method.
func (m *MockFoo) Bar(x int) int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Bar", x)
ret0, _ := ret[0].(int)
return ret0
}
// Bar indicates an expected call of Bar.
func (mr *MockFooMockRecorder) Bar(x interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bar", reflect.TypeOf((*MockFoo)(nil).Bar), x)
}
|
对生成的mock文件无需修改,生成后编写接口的单元测试方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package gomock
func TestFoo(t *testing.T) {
ctrl := gomock.NewController(t)
// Assert that Bar() is invoked.
defer ctrl.Finish()
m := NewMockFoo(ctrl)
// Asserts that the first and only call to Bar() is passed 99.
// Anything else will fail.
m.
EXPECT().
Bar(gomock.Eq(99)).
Return(101)
SUT(m)
}
|
gomock.NewController
:返回 gomock.Controller
,它代表 mock 生态系统中的顶级控件。定义了 mock 对象的范围、生命周期和期待值。另外它在多个 goroutine 中是安全的
NewMockFoo
:创建一个新的 mock 实例
m.EXPECT().Bar(gomock.Eq(99)).Return(101)
:这里有三个步骤,EXPECT()返回一个允许调用者设置期望和返回值的对象。Bar(gomock.Eq(99)) 是设置入参并调用 mock 实例中的方法。Return(101) 是设置先前调用的方法出参。简单来说,就是设置入参并调用,最后设置返回值
运行测试
1
2
3
4
5
6
|
> go test -mod=mod -v -run="TestFoo"
=== RUN TestFoo
--- PASS: TestFoo (0.00s)
PASS
ok tools/gomock 0.007s
|
参考