golang/mock
GitHub: golang/mock
gomock 是一个用于 Go 语言的 mock 框架,通过自动生成接口的 mock 代码来辅助单元测试中的依赖隔离。
Stars: 9356 | Forks: 604
# gomock
**2023 年 6 月更新**:_此仓库和工具已不再维护。
请转而查看 [go.uber.org/mock](https://github.com/uber/mock) 以获取维护中的分支。_
[][ci-runs] [][reference]
gomock 是一个针对 [Go 编程语言][golang]的 mocking 框架。它
与 Go 内置的 `testing` 包集成良好,但也可以在其他
场景下使用。
## 安装说明
一旦你[安装了 Go][golang-install],请安装 `mockgen` 工具。
**注意**:如果你还没有这么做,请务必将 `$GOPATH/bin` 添加到你的
`PATH` 中。
要获取最新发布的版本,请使用:
### Go 版本 < 1.16
```
GO111MODULE=on go get github.com/golang/mock/mockgen@v1.6.0
```
### Go 1.16+
```
go install github.com/golang/mock/mockgen@v1.6.0
```
如果你在 CI pipeline 中使用 `mockgen`,固定使用特定的 mockgen 版本可能更合适。你应该尽量保持库与用于生成你的 mock 的 mockgen 版本同步。
## 运行 mockgen
`mockgen` 有两种操作模式:source 和 reflect。
### Source 模式
Source 模式通过源文件生成 mock 接口。
它通过使用 -source 标志来启用。在该模式下可能
有用的其他标志包括 -imports 和 -aux_files。
示例:
```
mockgen -source=foo.go [other options]
```
### Reflect 模式
Reflect 模式通过构建一个使用反射来理解接口的程序来生成 mock 接口。
它通过传递两个非标志参数来启用:一个 import path,以及一个
以逗号分隔的符号列表。
你可以使用 "." 来引用当前路径的 package。
示例:
```
mockgen database/sql/driver Conn,Driver
# 方便与 `go:generate` 一起使用。
mockgen . Conn,Driver
```
### Flags
`mockgen` 命令用于根据包含要被 mock 的接口的 Go 源文件
为 mock 类生成源代码。
它支持以下标志:
- `-source`:一个包含要被 mock 的接口的文件。
- `-destination`:用于写入生成的源代码的文件。如果你
不设置此项,代码将被打印到标准输出。
- `-package`:用于生成的 mock 类
源代码的 package。如果你不设置此项,package 名称将是 `mock_` 加上
输入文件的 package。
- `-imports`:应在生成的
源代码中使用的显式 import 列表,指定为以逗号分隔的元素列表,
格式为 `foo=bar/baz`,其中 `bar/baz` 是被导入的 package,而 `foo` 是
在生成的源代码中用于该 package 的标识符。
- `-aux_files`:应被查阅以解析(例如定义在不同文件中的
嵌入式接口)的附加文件列表。这被
指定为以逗号分隔的元素列表,格式为
`foo=bar/baz.go`,其中 `bar/baz.go` 是源文件,而 `foo` 是
该文件被 -source 文件使用的 package 名称。
- `-build_flags`:(仅限 reflect 模式)原样传递给 `go build` 的标志。
- `-mock_names`:为生成的 mock 指定的自定义名称列表。这被
指定为以逗号分隔的元素列表,格式为
`Repository=MockSensorRepository,Endpoint=MockSensorEndpoint`,其中
`Repository` 是接口名称,而 `MockSensorRepository` 是所需的
mock 名称(mock 工厂方法和 mock recorder 将以 mock 命名)。
如果其中一个接口没有指定自定义名称,则将使用默认的
命名约定。
- `-self_package`:生成代码的完整 package import path。此
标志的目的是通过尝试包含其自身的 package 来防止生成的代码中出现 import 循环。
如果 mock 的 package 被设置为它的输入之一(通常是主要的那个)并且输出是 stdio 以至于
mockgen 无法检测到最终输出的 package,就会发生这种情况。设置此标志将
告诉 mockgen 要排除哪个 import。
- `-copyright_file`:版权文件,用于向生成的源代码添加版权头。
- `-debug_parser`:仅打印解析器结果。
- `-exec_only`:(reflect 模式)如果设置,执行此反射程序。
- `-prog_only`:(reflect 模式)仅生成反射程序;将其写入 stdout 并退出。
- `-write_package_comment`:如果为 true,则写入 package 文档注释 (godoc)。(默认为 true)
有关使用 `mockgen` 的示例,请参阅 `sample/` 目录。在简单
的情况下,你将只需要 `-source` 标志。
## 构建 Mock
```
type Foo interface {
Bar(x int) int
}
func SUT(f Foo) {
// ...
}
```
```
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)
}
```
如果你使用的是 Go 1.14+ 版本,mockgen 1.5.0+ 版本,并且
将 *testing.T 传递给 `gomock.NewController(t)`,你不再需要显式调用
`ctrl.Finish()`。它将通过自注册的
[Cleanup](https://pkg.go.dev/testing?tab=doc#T.Cleanup) 函数自动为你调用。
## 构建 Stubs
```
type Foo interface {
Bar(x int) int
}
func SUT(f Foo) {
// ...
}
```
```
func TestFoo(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
m := NewMockFoo(ctrl)
// Does not make any assertions. Executes the anonymous functions and returns
// its result when Bar is invoked with 99.
m.
EXPECT().
Bar(gomock.Eq(99)).
DoAndReturn(func(_ int) int {
time.Sleep(1*time.Second)
return 101
}).
AnyTimes()
// Does not make any assertions. Returns 103 when Bar is invoked with 101.
m.
EXPECT().
Bar(gomock.Eq(101)).
Return(103).
AnyTimes()
SUT(m)
}
```
## 修改失败消息
当 matcher 报告失败时,它会打印出接收到(`Got`)和
预期(`Want`)的值。
```
Got: [3]
Want: is equal to 2
Expected call at user_test.go:33 doesn't match the argument at index 1.
Got: [0 1 1 2 3]
Want: is equal to 1
```
### 修改 `Want`
`Want` 值来自 matcher 的 `String()` 方法。如果 matcher 的
默认输出不能满足你的需求,可以按如下方式进行修改:
```
gomock.WantFormatter(
gomock.StringerFunc(func() string { return "is equal to fifteen" }),
gomock.Eq(15),
)
```
这会将 `gomock.Eq(15)` matcher 针对 `Want:` 的输出从 `is equal
to 15` 修改为 `is equal to fifteen`。
### 修改 `Got`
`Got` 值来自对象的 `String()` 方法(如果可用)。
在某些情况下,对象的输出难以阅读(例如,`[]byte`),
如果测试能以不同的方式打印它会很有帮助。以下内容
修改了 `Got` 值的格式化方式:
```
gomock.GotFormatterAdapter(
gomock.GotFormatterFunc(func(i interface{}) string {
// Leading 0s
return fmt.Sprintf("%02d", i)
}),
gomock.Eq(15),
)
```
如果接收到的值是 `3`,那么它将被打印为 `03`。
## 调试错误
### reflect vendoring 错误
```
cannot find package "."
... github.com/golang/mock/mockgen/model
```
如果你在使用 reflect 模式和 vendoring
依赖项时遇到此错误,你可以选择三种变通方法:
1. 使用 source 模式。
2. 包含一个空导入 `import _ "github.com/golang/mock/mockgen/model"`。
3. 将 `--build_flags=--mod=mod` 添加到你的 mockgen 命令中。
此错误是由于在较新版本中 `go` 命令的默认行为发生更改所致。
更多细节可以在
[#494](https://github.com/golang/mock/issues/494) 中找到。
标签:EVTX分析, Go, Mock框架, Ruby工具, SOC Prime, 代码生成, 单元测试, 开发工具, 日志审计, 渗透测试工具