GoMonkey单元测试框架
GoMonkey单元测试框架
GoMonkey 是一个用于 Go 语言单元测试的打桩框架,能够帮助开发者在不修改原始代码的情况下,对函数、方法等进行动态替换,以便更好地进行单元测试
由于方法(成员函数)无法通过 GoStub 框架打桩,当产品代码的 OO 设计比较多时,打桩点可能离被测函数比较远,导致UT用例写起来比较难受。同时过程或函数通过 GoStub 框架打桩时,对产品代码有侵入性。Gomonkey框架解决了这部分问题
Gomokey通过在运行时通过汇编语句重写可执行文件,将待打桩函数或方法的实现跳转到桩实现,原理和热补丁类似。通过 Monkey,我们可以解决函数或方法的打桩问题,但 Monkey 不是线程安全的,不要将 Monkey 用于并发的测试中。
安装
使用以下命令安装 GoMonkey:
go get github.com/agiledragon/gomonkey
对于 v2.1.0 及以上版本,例如 v2.11.0,使用以下命令:
go get github.com/agiledragon/gomonkey/v2@v2.11.0
基本使用
1. 为函数打桩
ApplyFunc
将目标函数 target 替换为自定义实现 double。
patches := gomonkey.ApplyFunc(target, double)
defer patches.Reset()
target表示要被替换的原始函数。double表示替换后的函数,用它来模拟原始函数的行为。
例如,将 SomeFunction 替换为返回固定值 42 的函数:
patches := gomonkey.ApplyFunc(SomeFunction, func(arg int) int {
return 42
})
defer patches.Reset()
ApplyFuncReturn
将目标函数 target 替换为一个固定的返回值,而不需自定义函数逻辑。
patches := gomonkey.ApplyFuncReturn(target, output...)
defer patches.Reset()
target表示要替换的目标函数。output是变参形式,表示目标函数的返回值,可以指定一个或多个返回值。
2. 为公共成员方法打桩
ApplyMethod
将目标类型的公共成员方法 methodName 替换为自定义实现 double。
patches := gomonkey.ApplyMethod(reflect.TypeOf(receiver), methodName, double)
defer patches.Reset()
receiver是方法的接收者类型。methodName是要替换的方法名称。double是替换后的函数。
例如,将 MyStruct 的 myMethod 替换为返回 42 的函数:
type MyStruct struct{}
func (m *MyStruct) myMethod() int { return 10 }
patches := gomonkey.ApplyMethod(reflect.TypeOf(&MyStruct{}), "myMethod", func(_ *MyStruct) int {
return 42
})
defer patches.Reset()
ApplyMethodReturn
将目标类型的公共成员方法 methodName 替换为固定的返回值。
patches := gomonkey.ApplyMethodReturn(reflect.TypeOf(receiver), methodName, output...)
defer patches.Reset()
3. 为私有成员方法打桩
ApplyPrivateMethod
将目标类型的私有成员方法 methodName 替换为自定义实现 double。
patches := gomonkey.ApplyPrivateMethod(receiver, methodName, double)
defer patches.Reset()
receiver是方法的接收者类型。methodName是要替换的方法名称。double是替换后的函数。
例如,将 MyStruct 的私有方法 myPrivateMethod 替换为返回 42 的函数:
type MyStruct struct{}
func (m *MyStruct) myPrivateMethod() int { return 10 }
patches := gomonkey.ApplyPrivateMethod(&MyStruct{}, "myPrivateMethod", func(_ *MyStruct) int {
return 42
})
defer patches.Reset()
注意事项
- 关闭内联优化:GoMonkey 不支持内联函数,在测试时需要通过命令行参数
-gcflags=-l(Go 1.10 以下)或-gcflags=all=-l(Go 1.10 及以上)关闭 Go 语言的内联优化 - 线程安全:GoMonkey 不是线程安全的,所以不要把它用到并发的单元测试中
示例
假设有一个函数 SomeFunction 和一个结构体 MyStruct,我们来演示如何为它们打桩。
package main
import (
"fmt"
"github.com/agiledragon/gomonkey"
"reflect"
"testing"
)
func SomeFunction(arg int) int {
return arg * 2
}
type MyStruct struct{}
func (m *MyStruct) myMethod() int {
return 10
}
func TestSomeFunction(t *testing.T) {
// 为 SomeFunction 打桩,返回固定值 42
patches := gomonkey.ApplyFunc(SomeFunction, func(arg int) int {
return 42
})
defer patches.Reset()
result := SomeFunction(5)
if result != 42 {
t.Errorf("Expected 42, got %d", result)
}
}
func TestMyStructMethod(t *testing.T) {
// 为 MyStruct 的 myMethod 打桩,返回固定值 42
patches := gomonkey.ApplyMethod(reflect.TypeOf(&MyStruct{}), "myMethod", func(_ *MyStruct) int {
return 42
})
defer patches.Reset()
m := &MyStruct{}
result := m.myMethod()
if result != 42 {
t.Errorf("Expected 42, got %d", result)
}
}
通过以上示例,你可以看到如何使用 GoMonkey 为函数和方法打桩,从而在单元测试中模拟不同的行为和返回值。

浙公网安备 33010602011771号