gomock
gomock 是一个用于 Go 编程语言 的模拟框架。它与 Go 内置的 testing
包集成良好,但也可以在其他环境中使用。
该项目源自 Google 的 golang/mock
仓库。不幸的是,Google 不再维护这个项目,考虑到 Uber 内部大量使用 gomock 项目,我们决定在 Uber 进行分叉并继续维护。
欢迎以 GitHub issue 或 PR 的形式贡献!
支持的 Go 版本
go.uber.org/mock 支持官方 Go 发布策略 支持的所有 Go 版本。即最新的两个 Go 发行版。
安装
安装 mockgen
工具。
go install go.uber.org/mock/mockgen@latest
为确保安装正确,使用:
mockgen -version
如果失败,请确保你的 GOPATH/bin 在你的 PATH 中。你可以通过以下方式添加:
export PATH=$PATH:$(go env GOPATH)/bin
运行 mockgen
mockgen
有两种操作模式:源码模式和反射模式。
源码模式
源码模式从源文件生成模拟接口。通过使用 -source 标志启用。在此模式下,-imports 和 -aux_files 标志可能也很有用。
示例:
mockgen -source=foo.go [其他选项]
反射模式
反射模式通过构建一个使用反射来理解接口的程序来生成模拟接口。通过传递两个非标志参数来启用:一个导入路径和一个逗号分隔的符号列表。
你可以使用 "." 来引用当前路径的包。
示例:
mockgen database/sql/driver Conn,Driver
# 适用于 `go:generate`。
mockgen . Conn,Driver
标志
mockgen
命令用于根据包含要模拟的接口的 Go 源文件生成模拟类的源代码。它支持以下标志:
-
-source
:包含要模拟的接口的文件。 -
-destination
:要写入结果源代码的文件。如果不设置,代码将打印到标准输出。 -
-package
:用于结果模拟类源代码的包名。如果不设置,包名将是mock_
连接输入文件的包名。 -
-imports
:结果源代码中应使用的显式导入列表,指定为逗号分隔的foo=bar/baz
形式的元素列表,其中bar/baz
是正在导入的包,foo
是在生成的源代码中用于该包的标识符。 -
-aux_files
:应该查阅的额外文件列表,以解析例如在不同文件中定义的嵌入接口。这指定为逗号分隔的foo=bar/baz.go
形式的元素列表,其中bar/baz.go
是源文件,foo
是 -source 文件使用的该文件的包名。 -
-build_flags
:(仅反射模式)直接传递给go build
的标志。 -
-mock_names
:生成的模拟的自定义名称列表。这指定为逗号分隔的Repository=MockSensorRepository,Endpoint=MockSensorEndpoint
形式的元素列表,其中Repository
是接口名,MockSensorRepository
是所需的模拟名(模拟工厂方法和模拟记录器将以模拟命名)。如果其中一个接口没有指定自定义名称,则将使用默认命名约定。 -
-self_package
:生成代码的完整包导入路径。此标志的目的是通过尝试包含自己的包来防止生成的代码中的导入循环。如果模拟的包设置为其输入之一(通常是主要输入),并且输出是标准输出,因此 mockgen 无法检测最终输出包,就可能发生这种情况。设置此标志将告诉 mockgen 要排除哪个导入。 -
-copyright_file
:用于向结果源代码添加版权头的版权文件。 -
-debug_parser
:仅打印解析器结果。 -
-exec_only
:(反射模式)如果设置,执行此反射程序。 -
-prog_only
:(反射模式)仅生成反射程序;将其写入标准输出并退出。 -
-write_package_comment
:如果为 true,则写入包文档注释(godoc)。(默认为 true) -
-write_generate_directive
:添加 //go:generate 指令以重新生成模拟。(默认为 false) -
-write_source_comment
:如果为 true,则写入原始文件(源码模式)或接口名称(反射模式)注释。(默认为 true) -
-typed
:生成类型安全的 'Return'、'Do'、'DoAndReturn' 函数。(默认为 false) -
-exclude_interfaces
:要排除的接口的逗号分隔名称
有关 mockgen
使用的示例,请参见 sample/
目录。在简单情况下,你只需要 -source
标志。
构建模拟
type Foo interface {
Bar(x int) int
}
func SUT(f Foo) {
// ...
}
func TestFoo(t *testing.T) {
ctrl := gomock.NewController(t)
m := NewMockFoo(ctrl)
// 断言第一次也是唯一一次调用 Bar() 时传入 99。
// 其他任何情况都将失败。
m.
EXPECT().
Bar(gomock.Eq(99)).
Return(101)
SUT(m)
}
构建存根
type Foo interface {
Bar(x int) int
}
func SUT(f Foo) {
// ...
}
func TestFoo(t *testing.T) {
ctrl := gomock.NewController(t)
m := NewMockFoo(ctrl)
// 不做任何断言。当用 99 调用 Bar 时执行匿名函数并返回其结果。
m.
EXPECT().
Bar(gomock.Eq(99)).
DoAndReturn(func(_ int) int {
time.Sleep(1*time.Second)
return 101
}).
AnyTimes()
// 不做任何断言。当用 101 调用 Bar 时返回 103。
m.
EXPECT().
Bar(gomock.Eq(101)).
Return(103).
AnyTimes()
SUT(m)
}
修改失败消息
当匹配器报告失败时,它会打印收到的(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
值来自匹配器的 String()
方法。如果匹配器的默认输出不符合你的需求,可以按如下方式修改:
gomock.WantFormatter(
gomock.StringerFunc(func() string { return "is equal to fifteen" }),
gomock.Eq(15),
)
这将 gomock.Eq(15)
匹配器的 Want:
输出从 is equal to 15
修改为 is equal to fifteen
。
修改 Got
Got
值来自对象的 String()
方法(如果可用)。在某些情况下,对象的输出难以阅读(例如,[]byte
),为测试以不同方式打印它会有所帮助。以下修改了 Got
值的格式:
gomock.GotFormatterAdapter(
gomock.GotFormatterFunc(func(i any) string {
// 前导 0
return fmt.Sprintf("%02d", i)
}),
gomock.Eq(15),
)
如果收到的值是 3
,那么它将被打印为 03
。