
mock其实类似json 反序列化,有两种方式,一种是代码生成式的,代表是gomock,前面已经介绍过gomock 源码分析;另一种就是基于反射的源码替换方式,就是本文要介绍的gomonkey,其源码位于:github.com/agiledragon/gomonkey
在分析其源码之前,我们结合例子看看它是如何使用的:
package exp6_test
import (
"encoding/json"
"testing"
. "github.com/agiledragon/gomonkey"
"github.com/agiledragon/gomonkey/test/fake"
. "github.com/smartystreets/goconvey/convey"
)
var (
outputExpect = "xxx-vethName100-yyy"
)
func TestApplyFunc(t *testing.T) {
Convey("TestApplyFunc", t, func() {
Convey("one func for succ", func() {
patches := ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) {
return outputExpect, nil
})
defer patches.Reset()
output, err := fake.Exec("", "")
So(err, ShouldEqual, nil)
So(output, ShouldEqual, outputExpect)
})
Convey("one func for fail", func() {
patches := ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) {
return "", fake.ErrActual
})
defer patches.Reset()
output, err := fake.Exec("", "")
So(err, ShouldEqual, fake.ErrActual)
So(output, ShouldEqual, "")
})
Convey("two funcs", func() {
patches := ApplyFunc(fake.Exec, func(_ string, _ ...string) (string, error) {
return outputExpect, nil
})
defer patches.Reset()
patches.ApplyFunc(fake.Belong, func(_ string, _ []string) bool {
return true
})
output, err := fake.Exec("", "")
So(err, ShouldEqual, nil)
So(output, ShouldEqual, outputExpect)
flag := fake.Belong("", nil)
So(flag, ShouldBeTrue)
})
Convey("input and output param", func() {
patches := ApplyFunc(json.Unmarshal, func(_ []byte, v interface{}) error {
p := v.(*map[int]int)
*p = make(map[int]int)
(*p)[1] = 2
(*p)[2] = 4
return nil
})
defer patches.Reset()
var m map[int]int
err := json.Unmarshal(nil, &m)
So(err, ShouldEqual, nil)
So(m[1], ShouldEqual, 2)
So(m[2], ShouldEqual, 4)
})
})
}测试下
% go test -gcflags="all=-l -N" -v ./test/mockey/exp6/...
=== RUN TestApplyFunc
TestApplyFunc
one func for succ ✔✔
one func for fail ✔✔
two funcs ✔✔✔
input and output param ✔✔✔
10 total assertions
--- PASS: TestApplyFunc (0.00s)
PASS它一般和convey配合使用,由于gomonkey是基于符号表对函数指针进行替换来实现mock的,所以需要禁止内联优化
go test -gcflags="all=-l -N"它的核心方法如下,首先是进行方法函数/方法进行mock,ApplyMethod 接口定义如下:
func ApplyMethod(target reflect.Type, methodName string, double interface{}) *Patches
func (this *Patches) ApplyMethod(target reflect.Type, methodName string, double interface{}) *PatchesApplyMethod 第一个参数是目标类的指针变量的反射类型,第二个参数是字符串形式的方法名,第三个参数是桩函数。测试完成后,patches 对象通过 Reset 成员方法删除所有测试桩。
接着是mock变量和函数变量,ApplyGlobalVar 接口定义如下:
func ApplyGlobalVar(target, double interface{}) *Patches
func (this *Patches) ApplyGlobalVar(target, double interface{}) *PatchesApplyGlobalVar 第一个参数是全局变量的地址,第二个参数是全局变量的桩。测试完成后,patches 对象通过 Reset 成员方法删除所有测试桩。
ApplyFuncVar 接口定义如下:
func ApplyFuncVar(target, double interface{}) *Patches
func (this *Patches) ApplyFuncVar(target, double interface{}) *PatchesApplyFuncVar 第一个参数是函数变量的地址,第二个参数是桩函数。测试完成后,patches 对象通过 Reset 成员方法删除所有测试桩。
对应的,还有三个批量函数,ApplyFuncSeq 第一个参数是函数名,第二个参数是特定的桩序列参数。
func ApplyFuncSeq(target interface{}, outputs []OutputCell) *Patches
func (this *Patches) ApplyFuncSeq(target interface{}, outputs []OutputCell) *Patches其中,OutputCell 的定义为:
type Params []interface{}
type OutputCell struct {
Values Params
Times int
}测试完成后,patches 对象通过 Reset 成员方法删除所有测试桩。在单元测试中对一个函数进行多次调用并返回不同的结果。它适用于那些在测试中需要模拟函数在循环中被多次调用的场景。
patches := gomonkey.NewPatches()
defer patches.Reset()
// 定义桩函数序列patches.ApplyFuncSeq(funcA, []OutputCell{
{Values: Params{1}},
{Values: Params{2}},
{Values: Params{3}},
})ApplyMethodSeq 接口定义如下:
func ApplyMethodSeq(target reflect.Type, methodName string, outputs []OutputCell) *Patches
func (this *Patches) ApplyMethodSeq(target reflect.Type, methodName string, outputs []OutputCell) *PatchesApplyMethodSeq 第一个参数是目标类的指针变量的反射类型,第二个参数是字符串形式的方法名,第三参数是特定的桩序列参数。测试完成后,patches 对象通过 Reset 成员方法删除所有测试桩。
ApplyFuncVarSeq 接口定义如下:
func ApplyFuncVarSeq(target interface{}, outputs []OutputCell) *Patches
func (this *Patches) ApplyFuncVarSeq(target interface{}, outputs []OutputCell) *PatchesApplyFuncVarSeq 第一个参数是函数变量地址,第二个参数是特定的桩序列参数。测试完成后,patches 对象通过 Reset 成员方法删除所有测试桩。
核心方法介绍完了,最后我们看下创建打桩的方法:
func NewPatches() *PatchesNewPatches 是 patches 对象的显式构造函数,一般用于目标和桩的表驱动场景。测试完成后,patches 对象通过 Reset 成员方法删除所有测试桩。
本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!