golang生成自包含模拟对象工具插件库counterfeiter的使用
Golang生成自包含模拟对象工具插件库counterfeiter的使用
介绍
在为一个对象编写单元测试时,通常需要该对象协作者的假实现。在Go中,这样的假实现无法在运行时自动生成,手动编写可能相当繁琐。counterfeiter
允许你为给定接口简单地生成测试替身。
支持的Go版本
counterfeiter
遵循Go本身的支持策略:
每个主要的Go版本会一直支持到有两个更新的主要版本发布为止。例如,Go 1.5一直支持到Go 1.7发布,Go 1.6一直支持到Go 1.8发布。我们会根据需要发布小版本修订(例如Go 1.6.1、Go 1.6.2等)来修复受支持版本中的关键问题,包括关键安全问题。
使用counterfeiter
步骤1 - 添加counterfeiter作为工具依赖
通过运行以下命令建立对counterfeiter的工具依赖:
go get -tool github.com/maxbrunsfeld/counterfeiter/v6
步骤2a - 添加go:generate指令
你可以在接口定义旁边(或不在旁边)添加指令,放在模块中的任何.go
文件中。
package foo
//go:generate go tool counterfeiter . MySpecialInterface
type MySpecialInterface interface {
DoThings(string, uint64) (int, error)
}
然后运行:
$ go generate ./...
Writing `FakeMySpecialInterface` to `foofakes/fake_my_special_interface.go`... Done
步骤2b - 添加counterfeiter:generate指令
如果你计划在一个包中有多个指令,考虑使用这个选项,因为它会大大加快速度。
package foo
// You only need **one** of these per package!
//go:generate go tool counterfeiter -generate
// You will add lots of directives like these in the same package...
//counterfeiter:generate . MySpecialInterface
type MySpecialInterface interface {
DoThings(string, uint64) (int, error)
}
// Like this...
//counterfeiter:generate . MyOtherInterface
type MyOtherInterface interface {
DoOtherThings(string, uint64) (int, error)
}
然后运行:
$ go generate ./...
Writing `FakeMySpecialInterface` to `foofakes/fake_my_special_interface.go`... Done
Writing `FakeMyOtherInterface` to `foofakes/fake_my_other_interface.go`... Done
步骤3 - 运行go generate
你可以在包含指令的目录中运行go generate
,或者在模块的根目录中运行(以确保为模块中的所有包生成)。
$ go generate ./...
从shell调用counterfeiter
你可以使用以下命令在go模块中调用counterfeiter
:
$ go tool counterfeiter
USAGE
counterfeiter
[-generate>] [-o <output-path>] [-p] [--fake-name <fake-name>]
[-header <header-file>]
[<source-path>] <interface> [-]
生成测试替身
给定包的路径和接口名称,你可以生成一个测试替身。
package foo
type MySpecialInterface interface {
DoThings(string, uint64) (int, error)
}
$ go tool counterfeiter path/to/foo MySpecialInterface
Wrote `FakeMySpecialInterface` to `path/to/foo/foofakes/fake_my_special_interface.go`
在测试中使用测试替身
实例化假对象:
import "my-repo/path/to/foo/foofakes"
var fake = &foofakes.FakeMySpecialInterface{}
假对象记录它们被调用的参数:
fake.DoThings("stuff", 5)
Expect(fake.DoThingsCallCount()).To(Equal(1))
str, num := fake.DoThingsArgsForCall(0)
Expect(str).To(Equal("stuff"))
Expect(num).To(Equal(uint64(5)))
你可以存根它们的返回值:
fake.DoThingsReturns(3, errors.New("the-error"))
num, err := fake.DoThings("stuff", 5)
Expect(num).To(Equal(3))
Expect(err).To(Equal(errors.New("the-error")))
为第三方接口生成测试替身
对于第三方接口,你可以使用替代语法<package>.<interface>
指定接口,例如:
$ go tool counterfeiter github.com/go-redis/redis.Pipeliner
运行counterfeiter的测试
如果你想运行counterfeiter
的测试(也许是因为你想贡献一个PR),你只需要运行scripts/ci.sh
。
贡献
如果你想贡献counterfeiter
,请遵循以下步骤:
- 打开一个新的github issue,描述你的问题或用例
- 帮助我们理解你想如何修复或扩展
counterfeiter
- 为你想要的行为编写一个或多个单元测试
- 为你正在处理的功能编写最简单的代码
- 尝试寻找重构的机会
- 避免编写没有单元测试覆盖的代码
许可证
counterfeiter
是MIT许可的。
更多关于golang生成自包含模拟对象工具插件库counterfeiter的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang生成自包含模拟对象工具插件库counterfeiter的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 模拟对象工具 Counterfeiter 使用指南
Counterfeiter 是一个用于 Go 语言的模拟对象生成工具,它可以自动为接口生成模拟实现,非常适合单元测试场景。
安装 Counterfeiter
go install github.com/maxbrunsfeld/counterfeiter/v6@latest
基本使用
1. 为接口生成模拟实现
假设我们有以下接口定义:
// 在文件 mypackage/myinterface.go 中
package mypackage
type MyInterface interface {
DoSomething(int) string
DoSomethingElse(string) (string, error)
}
运行以下命令生成模拟实现:
counterfeiter -generate mypackage/myinterface.go
这将生成一个 mypackagefakes/fake_my_interface.go
文件,其中包含 FakeMyInterface
结构体。
2. 在测试中使用模拟对象
package mypackage_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/yourproject/mypackage"
"github.com/yourproject/mypackage/mypackagefakes"
)
var _ = Describe("MyService", func() {
var (
fakeMyInterface *mypackagefakes.FakeMyInterface
myService *mypackage.MyService
)
BeforeEach(func() {
fakeMyInterface = new(mypackagefakes.FakeMyInterface)
myService = mypackage.NewMyService(fakeMyInterface)
})
It("should call DoSomething with correct parameters", func() {
fakeMyInterface.DoSomethingReturns("fake result")
result := myService.Execute(42)
Expect(result).To(Equal("fake result"))
Expect(fakeMyInterface.DoSomethingCallCount()).To(Equal(1))
arg := fakeMyInterface.DoSomethingArgsForCall(0)
Expect(arg).To(Equal(42))
})
})
高级功能
1. 自定义返回值
// 设置返回值
fakeMyInterface.DoSomethingReturns("custom result")
// 设置多个返回值(对有返回error的方法)
fakeMyInterface.DoSomethingElseReturns("result", errors.New("error"))
// 使用回调函数动态返回值
fakeMyInterface.DoSomethingStub = func(arg int) string {
if arg > 10 {
return "big"
}
return "small"
}
2. 验证调用参数
// 验证调用次数
Expect(fakeMyInterface.DoSomethingCallCount()).To(Equal(1))
// 获取特定调用的参数
arg := fakeMyInterface.DoSomethingArgsForCall(0)
Expect(arg).To(Equal(42))
3. 生成命令选项
# 为特定接口生成模拟
counterfeiter . MyInterface
# 指定输出目录
counterfeiter -o ./testfakes mypackage/myinterface.go
# 在Go generate中使用
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . MyInterface
最佳实践
- 保持模拟简单:只模拟必要的接口,避免过度模拟
- 明确命名:使用清晰的命名区分真实实现和模拟实现
- 验证交互:不仅要验证返回值,还要验证是否正确调用了依赖
- 重置状态:在测试之间重置模拟对象的状态
与其他工具集成
Counterfeiter 可以与 Ginkgo/Gomega 等测试框架完美配合:
It("should handle errors", func() {
fakeMyInterface.DoSomethingElseReturns("", errors.New("failed"))
_, err := myService.ExecuteElse("input")
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError("failed"))
})
Counterfeiter 是 Go 生态中功能强大且易于使用的模拟工具,能够显著提高单元测试的编写效率和质量。