Mockey
English | 中文
Mockey 是一个简单易用的 Golang mock 库,可以快速方便地模拟函数和变量。目前广泛应用于字节跳动服务的单元测试编写中。底层通过在运行时重写函数指令实现 monkey patch。
安装
go get github.com/bytedance/mockey@latest
快速入门
import (
"fmt"
"testing"
. "github.com/bytedance/mockey"
. "github.com/smartystreets/goconvey/convey"
)
func Foo(in string) string {
return in
}
type A struct{}
func (a A) Foo(in string) string { return in }
var Bar = 0
func TestMockXXX(t *testing.T) {
PatchConvey("TestMockXXX", t, func() {
Mock(Foo).Return("c").Build() // 模拟函数
Mock(A.Foo).Return("c").Build() // 模拟方法
MockValue(&Bar).To(1) // 模拟变量
So(Foo("a"), ShouldEqual, "c") // 断言 `Foo` 被模拟
So(new(A).Foo("b"), ShouldEqual, "c") // 断言 `A.Foo` 被模拟
So(Bar, ShouldEqual, 1) // 断言 `Bar` 被模拟
})
// `PatchConvey` 外部自动释放模拟
fmt.Println(Foo("a")) // a
fmt.Println(new(A).Foo("b")) // b
fmt.Println(Bar) // 0
}
特性
- 模拟函数和方法
- 基础
- 普通/可变参数函数
- 普通/可变参数方法
- 嵌套结构体方法
- 私有类型的导出方法(不同包下)
- 高级
- 模拟后执行原函数
- 协程条件过滤
- 增量式改变模拟行为
- 获取目标函数执行次数
- 获取模拟函数执行次数
- 基础
- 模拟变量
- 普通变量
- 函数变量
兼容性
操作系统支持
- Mac OS(Darwin)
- Linux
- Windows
架构支持
- AMD64
- ARM64
版本支持
- Go 1.13+
许可证
Mockey 采用 Apache License 2.0 版本进行分发。Mockey 第三方依赖的许可证说明可在此处查看。
FAQ
Go 1.23 编译出错 "link: github.com/bytedance/mockey/internal/monkey/common: invalid reference to runtime.sysAllocOS"
?
添加构建标志 -ldflags=-checklinkname=0
如何禁用内联和编译优化?
- 命令行:
go test -gcflags="all=-l -N" -v ./...
- Goland:在 Run/Debug Configurations > Go tool arguments 对话框中填入
-gcflags="all=-l -N"
模拟后仍然进入原函数?
- 未禁用内联或编译优化:可以尝试使用 debug 模式,如果能跑通说明是这个问题,请前往 FAQ 的相关章节
- 未调用
Build()
方法:忘记调用Build()
,导致没有实际生效 - 目标函数不完全匹配:
func TestXXX(t *testing.T) {
Mock((*A).Foo).Return("c").Build()
fmt.Println(A{}.Foo("a")) // 进入原函数,因为模拟目标应该是 `A.Foo`
a := A{}
Mock(a.Foo).Return("c").Build()
fmt.Println(a.Foo("a")) // 进入原函数,因为模拟目标应该是 `A.Foo` 或使用 `GetMethod` 从实例 `a` 中提取
}
- 目标函数在其他协程中执行:
func TestXXX(t *testing.T) {
PatchConvey("TestXXX", t, func() {
Mock(Foo).Return("c").Build()
go Foo("a") // 执行 'foo' 的时机不确定
})
// 主协程走到这里时,相关模拟已被 'PatchConvey' 释放。如果 'foo' 在此之前执行,则模拟成功,否则失败
fmt.Println("over")
time.Sleep(time.Second)
}