go monkey和gostub打桩工具
2022-01-10 本文已影响0人
彳亍口巴
使用gostub进行打桩
GoStub是一个单元测试中的打桩工具,它支持为全局变量、函数等打桩。
安装
go get github.com/prashantv/gostub
使用示例
package main
import (
"encoding/json"
"fmt"
)
var maxNum int
type Person struct {
Id int
name string
}
func main() {
}
func Func01() int {
res, _ := json.Marshal(Person{
Id: 1, name: "string",
})
var person Person
err := json.Unmarshal(res, &person)
if err != nil {
fmt.Println(err)
}
return RPCFunc()+maxNum
}
var RPCFunc = func() int {
fmt.Println("调用rpc")
return maxNum
}
//测试代码
package main
import (
"github.com/prashantv/gostub"
"testing"
)
func TestFunc01(t *testing.T) {
tests := []struct {
name string
want int
}{
// TODO: Add test cases.
{name: "test01", want: 21},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 对RPCFunc函数变量打桩,返回结果为1
stubs := gostub.StubFunc(&RPCFunc, 1)
// 对变量maxNum打桩,使得maxNum每次都是20
stubs = stubs.Stub(&maxNum, 20)
defer stubs.Reset()
if got := Func01(); got != tt.want {
t.Errorf("Func01() = %v, want %v", got, tt.want)
}
})
}
}
执行结果go test -v
:
=== RUN TestFunc01
=== RUN TestFunc01/test01
--- PASS: TestFunc01 (0.00s)
--- PASS: TestFunc01/test01 (0.00s)
PASS
ok smalltest/go_test/stub_test 0.152s
使用monkey进行打桩
介绍
monkey是一个Go单元测试中十分常用的打桩工具,它在运行时通过汇编语言重写可执行文件,将目标函数或方法的实现跳转到桩实现,其原理类似于热补丁。
monkey库很强大,但是使用时需注意以下事项:
- monkey不支持内联函数,在测试的时候需要通过命令行参数
-gcflags=-l
关闭Go语言的内联优化。 - monkey不是线程安全的,所以不要把它用到并发的单元测试中。
安装
go get bou.ke/monkey
使用示例
package main
import (
"fmt"
"time"
)
func GetAge(a int64) int64 {
return CalcAge(a) + a
}
func CalcAge(a int64) int64 {
fmt.Println("调用CalcAge")
if a == 0 {
return 1
}
return 20
}
type User struct {
Name string
Birthday string
}
// CalcAge 计算用户年龄
func (u *User) CalcAge(a int64) int {
fmt.Println("调用")
t, err := time.Parse("2006-01-02", u.Birthday)
if err != nil {
return -1
}
return int(time.Now().Sub(t).Hours()/24.0) / 365
}
// GetInfo 获取用户相关信息
func (u *User) GetInfo(a int64) string {
fmt.Println(a)
age := u.CalcAge(a)
if age <= 0 {
return fmt.Sprintf("%s很神秘,我们还不了解ta。", u.Name)
}
return fmt.Sprintf("%s今年%d岁了,ta是我们的朋友。", u.Name, age)
}
测试代码
package main
import (
"fmt"
"reflect"
"testing"
"bou.ke/monkey"
)
func TestGetAge(t *testing.T) {
monkey.Patch(CalcAge, func(a int64) int64 {
return 100
})
type args struct {
a int64
}
tests := []struct {
name string
args args
want int64
}{
// TODO: Add test cases.
{name: "monkey_1", args: args{10}, want: 110},
{name: "monkey_2", args: args{80}, want: 180},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetAge(tt.args.a); got != tt.want {
t.Errorf("GetAge() = %v, want %v", got, tt.want)
}
})
}
}
func TestUser_GetInfo(t *testing.T) {
type fields struct {
Name string
Birthday string
}
type args struct {
a int64
}
tests := []struct {
name string
fields fields
args args
want string
}{
// TODO: Add test cases.
{name: "getInfo_1", fields: fields{Name: "zzr", Birthday: "1995-03-08"}, args: args{a: 10}, want: fmt.Sprintf("%s今年%d岁了,ta是我们的朋友。", "zzr", 18)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
u := &User{
Name: tt.fields.Name,
Birthday: tt.fields.Birthday,
}
// 为对象方法打桩
monkey.PatchInstanceMethod(reflect.TypeOf(u), "CalcAge", func(*User, int64) int {
return 18
})
if got := u.GetInfo(tt.args.a); got != tt.want {
t.Errorf("GetInfo() = %v, want %v", got, tt.want)
}
})
}
}
monkey不仅可以对函数进行打桩,也可以对方法进行打桩,上述代码演示了其为函数以及方法打桩的过程