写出可测试 的代码 至关重要. 可以保证代码的稳定性. 帮助程序员减少bug.
gomock 是一个go官方的模拟框架.
gomock的使用场景:
- IO类型的数据, 本地文件,数据库,网络API,RPC等
- 依赖的服务还没有开发好, 这时候可以自己模拟一个服务, 加快开发进度提升开发效率
- 压力性能测试的时候屏蔽外部依赖, 专注测试本模块
- 依赖的内部函数非常复杂, 要构造数据非常不方便,这也是一种
install
go get github.com/golang/mock
go install github.com/golang/mock/mockgen@latest
使用
gomock在许多 开源项目中都有使用. 可以参考一些开源项目。 比如 [gocache](https://github.com/eko/gocache)
使用mockgen 支持两种 自动生成代码的方式.
指定source,从源文件生成 mock接口
mockgen -source=./foo.go -destination=../test/mock/ -pakcage=mock
通过
reflect
的方式, 这种方式需要传递两种非标志参数来启动.导入路径 import
和逗号分隔的需要mock的接口列表
mockgen database/sql/driver Conn,Driver
mockgen 相关参数
-source
: 要模拟的接口文件-desitination
: mock文件输出的地方,如不设置,默认输出到标准输出中.-package
: 生成的 mock 文件的包名, 如不设置则为mock_
前缀加上输入的文件名。-imports
: 应该在生成的源码文件中显示的导入的包列表. 声明为foo=bar/baz
形式的并且以逗号分隔的列表.foo
表示生成的源码文件中报的标识符,bar/baz
是要导入的包.-aux_files
: 应查阅的附加文件列表, 已解决例如在不同文件中定义的嵌入式接口(embbed interface). 声明为foo=bar/baz.go
形式的并且以逗号分隔的列表.bar/baz.go
是指定的源文件.foo
是-source
指定的文件的包名.-build_flags
: 这个参数只在 reflect模式下使用. 用于go build
中使用.-mock_names
: 自定义生成mock的文件列表. 使用逗号分隔. 例如:Repository=MockSensorRepository,Endpoint=MockSensorEndpoint
. Repository Endpoint 是要模拟的接口,MockSensorRepository
和MockSensorEndpoint
是模拟的实现类名. 如果不指定,则使用默认值.
Example
gomock.NewController
: 返回 gomock.Controller,它代表 mock 生态系统中的顶级控件。定义了 mock 对象的范围、生命周期和期待值。另外它在多个 goroutine 中是安全的mock.NewMockMale
: 创建一个新的 mock 实例gomock.InOrder
: 声明给定的调用应按顺序进行(是对 gomock.After 的二次封装)mockMale.EXPECT().Get(id).Return(nil)
: 这里有三个步骤,EXPECT()返回一个允许调用者设置期望和返回值的对象. Get(id) 是设置入参并调用 mock 实例中的方法. Return(nil) 是设置先前调用的方法出参。简单来说,就是设置入参并调用,最后设置返回值NewUser(mockMale)
: 创建 User 实例, 值得注意的是,在这里注入了 mock 对象,因此实际在随后的 user.GetUserInfo(id) 调用(入参:id 为 1)中. 它调用的是我们事先模拟好的 mock 方法ctrl.Finish()
: 进行 mock 用例的期望值断言,一般会使用 defer 延迟执行,以防止我们忘记这一操作
参数匹配器
[Matcher](https://github.com/golang/mock/blob/main/gomock/matchers.go#L25)
表示values 的类型. 通常用于表示 mock方法的期望参数.
Matcher接口定义:
// A Matcher is a representation of a class of values.
// It is used to represent the valid or expected arguments to a mocked method.
type Matcher interface {
// Matches returns whether x is a match.
Matches(x interface{}) bool
// String describes what the matcher matches.
String() string
}
有时,不关心 调用mock时的特定参数,使用 gomock, 可以预期参数 具有固定值(通过 指定预期的 参数值),与谓词匹配, 称为匹配器.
gomock.Any():匹配任何值(任何类型)
gomock.Eq(x):使用反射来匹配是值DeepEqual 到 x
gomock.Nil(): 火柴 nil
gomock.Not(m):(m 匹配器在哪里)匹配匹配器不匹配的值 m, gomock.Not(x)(式中, x 是 不 一个Matcher)匹配的值不 DeepEqual 至 x
需要掌握的
- 使用
mockgen
生成代码 一节 使用go:generate
批量生成代码 - 了解 gomock 是对接口的mock
- 期望入参
- 期望返回值
- 调用次数
- 调用顺序
- 执行
Do
mock逻辑