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) 以获取维护中的分支。_ [![Build Status](https://static.pigsec.cn/wp-content/uploads/repos/cas/b2/b2a5d799b97d961b7e9fa5570d19cc1fb35d01733a0f4314a30258ee95fc7030.svg)][ci-runs] [![Go Reference](https://pkg.go.dev/badge/github.com/golang/mock.svg)][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, 代码生成, 单元测试, 开发工具, 日志审计, 渗透测试工具