agiledragon / gomonkey Goto Github PK
View Code? Open in Web Editor NEWgomonkey is a library to make monkey patching in unit tests easy
License: MIT License
gomonkey is a library to make monkey patching in unit tests easy
License: MIT License
您好,在业务中需要对http请求进行mock,所以对http.Client的Do方法进行打桩,但是用如下的代码打桩之后,发现在实际的使用中并没有生效。想请大神帮忙看看为什么
// http请求打桩
var c *http.Client
patches := gomonkey.ApplyMethod(reflect.TypeOf(c), "Do",func(c *http.Client, req *http.Request) (*http.Response, error){
rsp := &http.Response{}
if strings.HasPrefix(req.URL.Path, "/fcgi-bin/zx_get_seqno.fcgi") {
rsp.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(`{"retcode":"0","retmsg`)))
} else if strings.HasPrefix(req.URL.Path, "/fcgi-bin/zx_put_file.fcgi") {
rsp.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(`{"retcode":"0","r`)))
} else if strings.HasPrefix(req.URL.Path, "/fcgi-bin/zx_sm_recognition.fcgi") {
rsp.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(`{"retcode":"0","r`)))
} else {
return nil, nil
}
return rsp, nil
})
defer patches.Reset()
I want to run it on my apple m1,but it can not work.
panic: double seq is less than call seq [recovered]
panic: double seq is less than call seq
package main
import (
"fmt"
"reflect"
"github.com/agiledragon/gomonkey"
)
type B struct {
a string
}
func (b *B) Get(s string) string {
return ""
}
func (b *B) Put(s string) string {
var i int
for ; i < 100; i++ {
b.a = "iiiii"
}
return ""
}
func main(){
var b *B
gomonkey.ApplyMethod(reflect.TypeOf(b), "Get",
func(_ *B, subPath string) string {
return subPath
})
fmt.Println("get:", ws.Get("get")) // want "get"
gomonkey.ApplyMethod(reflect.TypeOf(b), "Put",
func(_ *B, subPath string) string {
return subPath
})
fmt.Println("put:", ws.Put("put"))
}
b.Get没被替换
When I execute go get github.com/agiledragon/gomonkey
, I got following error:
go get github.com/agiledragon/gomonkey: no matching versions for query "upgrade"
Supported Platform:
Does gomonkey supports apple silicon m1 right now? Or other thing going wrong?
type Config interface {
Load() error
Reload()
Get(string, interface{}) interface{}
Unmarshal(interface{}) error
IsSet(string) bool
GetInt(string, int) int
GetInt32(string, int32) int32
GetFloat64(string, float64) float64
GetString(string, string) string
GetBool(string, bool) bool
}
var myConf config.Config
func foo() bool {
myConf.GetString("test", "1234")
return true
}
func main() {
// init myConf
}
对myConf.GetString mock,报错。mock
type mockConf struct{ test int }
func (conf mockConf) Load() error { panic("implement me") }
func (conf mockConf) Reload() { panic("implement me") }
func (conf mockConf) Get(string, interface{}) interface{} { panic("implement me") }
func (conf mockConf) Unmarshal(interface{}) error { panic("implement me") }
func (conf mockConf) IsSet(string) bool { panic("implement me") }
func (conf mockConf) GetInt(string, int) int { panic("implement me") }
func (conf mockConf) GetInt32(string, int32) int32 { panic("implement me") }
func (conf mockConf) GetFloat64(string, float64) float64 { panic("implement me") }
func (conf mockConf) GetString(string, string) string { panic("implement me") }
func (conf mockConf) GetBool(string, bool) bool { panic("implement me") }
func TestUnit_foo(t *testing.T) {
mConf := mockConf{test: 231}
p := gomonkey.ApplyGlobalVar(&myConf, mConf)
defer p.Reset() // !!! 注释这一行就正常,不注释报错
convey.Convey(" case 1", t, func() {
patch := gomonkey.ApplyMethod(reflect.TypeOf(mConf), "GetString",
func(_ mockConf, _ string, _ string) string {
return "hahaha"
})
defer patch.Reset()
result, _ := foo()
convey.So(result, convey.ShouldBeTrue)
})
}
报错信息为
2020/04/10 16:25:08 maxprocs: Leaving GOMAXPROCS=28: CPU quota undefined
=== RUN TestUnit_foo
1 total assertion
--- FAIL: TestUnit_foo (0.00s)
panic: reflect: call of reflect.flag.mustBeExported on zero Value [recovered]
panic: reflect: call of reflect.flag.mustBeExported on zero Value
goroutine 42 [running]:
testing.tRunner.func1.1(0xcab700, 0xc000392720)
/home/test/usrpkg/goroot/src/testing/testing.go:941 +0x3d0
testing.tRunner.func1(0xc000394480)
/home/test/usrpkg/goroot/src/testing/testing.go:944 +0x3f9
panic(0xcab700, 0xc000392720)
/home/test/usrpkg/goroot/src/runtime/panic.go:967 +0x15d
reflect.flag.mustBeExportedSlow(0x0)
/home/test/usrpkg/goroot/src/reflect/value.go:222 +0xad
reflect.flag.mustBeExported(...)
/home/test/usrpkg/goroot/src/reflect/value.go:216
reflect.Value.Set(0xd43a40, 0x17b1fb0, 0x194, 0x0, 0x0, 0x0)
/home/test/usrpkg/goroot/src/reflect/value.go:1527 +0x56
github.com/agiledragon/gomonkey.(*Patches).Reset(0xc000388600)
/home/test/usrpkg/gopath/pkg/mod/github.com/agiledragon/[email protected]/patch.go:131 +0x20d
mygit.com/foo.TestUnit_foo.func1(0xc000394480, 0xc0003842e8, 0xc000388600)
/mnt/d/foo/foo_test.go:217 +0xab
mygit.com/foo.TestUnit_foo(0xc000394480)
/mnt/d/foo/foo_test.go:232 +0x432
testing.tRunner(0xc000394480, 0xe494d8)
/home/test/usrpkg/goroot/src/testing/testing.go:992 +0xdc
created by testing.(*T).Run
/home/test/usrpkg/goroot/src/testing/testing.go:1043 +0x357
FAIL mygit.com/foo 0.042s
FAIL
type test struct{
Name string
}
func(t *test) printName(){
fmt.Println(t.Name)
}
// Err:“retrieve method by name failed”
ApplyMethod(reflect.TypeOf(s), "printName", func(_ *test) {
})
// 原因:ApplyMethod 底层调用reflect.TypeOf.MethodByName(methodName string), 若methodName为首字母小写,MethodByName 返回false。
package main
import (
"fmt"
"reflect"
)
type test struct{
Name string
}
func(t *test) printName(){
fmt.Println(t.Name)
}
func main() {
t := &test{}
_,ok:=reflect.TypeOf(t).MethodByName("printName")
fmt.Println(ok) // out:false
}
my environment:
As my title description, it work well until i encountered the method :
func (c *Controller) ServeJSON(encoding ...bool) {
var (
hasIndent = BConfig.RunMode != PROD
hasEncoding = len(encoding) > 0 && encoding[0]
)
c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding)
}
even though i use the -gcflags=all=-l , it's doesn't work as well.
There is my test code:
var c *myController
patches := ApplyMethod(reflect.TypeOf(c), "ServeJSON",func(*myController, ...bool) {
//do nothing
})
defer patches.Reset()
see the result as below :
panic(0x494ab60, 0x53e96d0)
/usr/local/go/src/runtime/panic.go:969 +0x191
github.com/astaxie/beego/context.(*BeegoOutput).Header(0xc00031e6a0, 0x4a7cca5, 0xc, 0x4a93a96, 0x1f)
/Users/xxx/go/src/github.com/astaxie/beego/context/output.go:58 +0x25
github.com/astaxie/beego/context.(*BeegoOutput).JSON(0xc00031e6a0, 0x49c2ba0, 0xc00037ca20, 0xc000380000, 0xc00038a128, 0xc00038a120)
/Users/xxx/go/src/github.com/astaxie/beego/context/output.go:189 +0x6a
github.com/astaxie/beego.(*Controller).ServeJSON(0xc0001c42a0, 0x0, 0x0, 0x0)
/Users/xxx/go/src/github.com/astaxie/beego/controller.go:357 +0xdc
How to fix this problem or recommand me a HTTP unit test framework ? Thanks!!!!
代码是这样的,单测失败了,应该是没mock成功,麻烦看一下哈
package main
import (
"reflect"
"testing"
"git.code.oa.com/jiayanlin/go_test/fake"
"github.com/agiledragon/gomonkey/v2"
. "github.com/smartystreets/goconvey/convey"
)
func TestApplyInterfaceReused(t *testing.T) {
e := &fake.Etcd{}
Convey("TestApplyInterfaceReused", t, func() {
patches := gomonkey.ApplyFunc(fake.NewDb, func(_ string) fake.Db {
return e
})
defer patches.Reset()
db := fake.NewDb("mysql")
Convey("TestApplyInterface", func() {
info := "hello interface"
patches.ApplyMethod(reflect.TypeOf(e), "Retrieve",
func(_ *fake.Etcd, _ string) (string, error) {
return info, nil
})
output, err := db.Retrieve("")
So(err, ShouldEqual, nil)
So(output, ShouldEqual, info)
})
})
}
跑出来后结果是这样的
* D:/go-projects/go_test/apply_interface_reused_test.go Line 30: Expected: 'hello interface' Actual: 'Hello, Mysql!' (Should be equal) goroutine 6 [running]: E:/Go/bin/pkg/mod/github.com/smartystreets/[email protected]/convey/reporting/reports.go:143 +0x74 E:/Go/bin/pkg/mod/github.com/smartystreets/[email protected]/convey/reporting/reports.go:103 +0x73
func TestA(t *testing.T) {
Convey("TestA", t, func() {
Convey(" test1", func() {
Patches := ApplyFunc(upgrade, func(_ int ,_ uint) int{
return 10
})
defer Patches.Reset()
})
}
func upgrade(age int, num uint) int {
}
func TestB(t *testing.T){
Cover("TestB",t,func(){
res := upgrade(1,1) //it should be return 11 but affect by mock in TestA
So(res, ShouldEqual, 11)
})
func A() {
fmt.Println("real function")
}
func TestA(t *testing.T) {
gomonkey.ApplyFunc(A, func() {
fmt.Println("mock function")
})
A()
}
when run this test case, it print real function
when debug this test case, it print mock function
Can't mock repeat?
Here is my demo function to be mocked:
func FuncDo(str string) (string, error) {
if err := ToBeMock(str); err != nil {
return "", err
}
return fmt.Sprintf("hello, %s", str), nil
}
func ToBeMock(str string) error {
return nil
}
And then I write my test unit:
func TestFuncDo(t *testing.T) {
patches := gomonkey.NewPatches()
defer patches.Reset()
patchesBuilder := dsl.NewPatchBuilder(patches)
// the 1st way
/*patchesBuilder.Func(ToBeMock).
Stubs().
With(dsl.Any()).
Will(dsl.Return(nil)).
Then(dsl.Repeat(dsl.Return(fmt.Errorf("error")), 1)).
End()*/
tests := []struct{
name string
mock func()
wantErr bool
} {
{
name: "success",
mock: func() {
// the 2nd way
patchesBuilder.Func(ToBeMock).
Stubs().
With(dsl.Any()).
Will(dsl.Repeat(dsl.Return(nil), 1)).
End()
},
wantErr: false,
},
{
name: "error",
mock: func() {
patchesBuilder.Func(ToBeMock).
Stubs().
With(dsl.Any()).
Will(dsl.Return(fmt.Errorf("error"))).
End()
},
wantErr: true,
},
}
for _, obj := range tests {
obj.mock()
t.Logf("===RUN\tTestDo3/%s", obj.name)
_, err := FuncDo("str")
if (err != nil) != obj.wantErr {
t.Errorf("wantErr %v, actual: %v", obj.wantErr, err)
}
}
}
but no matter I use the first way or the second way, I can't achieve the expected results(with 1st way, the result setted in "Then" don't take effect, and with 2nd way, it will throw a painc: panic: patch has been existed
)
how can I use v2 to get the expected results?
在开发的业务场景中,有时候会遇到中间有一个函数中有一个协程,类似下面这种
func testGoroutine() {
go PrintNum()
time.Sleep(time.Second * 1)
}
// PrintNum printnum
func PrintNum() {
for i := 0; i < 5; i++ {
fmt.Print(i)
}
}
如果使用gomonkey对PrintNum进行mock,代码如下:
func Test_testGoroutine(t *testing.T) {
Convey("test function", t, func() {
patches1 := ApplyFunc(PrintNum, func() {
fmt.Print("mock")
})
defer patches1.Reset()
testGoroutine()
})
}
但因为主线程可能提前Reset的缘故,PrintNum函数可能没有Mock成功,但是不Reset,在全包进行测试的时候,会不会影响其他函数测试,
虽然我也看到gomonkey不能用于并发,但还是想请问这种情况有什么好的解决方案吗?多谢啦
如下代码,并发访问getMockTime(),有安全问题吗?想确认下,我测试没发现并发问题。
func init() {
gomonkey.ApplyFunc(time.Now, func() time.Time {
mockTime := int64(1588239281376264700)
sec := mockTime / 1000000000
nsec := mockTime % 1000000000
return time.Unix(sec, nsec)
})
}
func getMockTime() {
// output: 2020-04-30 17:34:41.3762647 +0800 CST
fmt.Println("mock time:", time.Now())
}
After I have mocked a function A by function B, is there any way to call original A function inside B?
Which might be helpful for user to check or log arguments and return values.
Thanks!
如果我用B函数替换了对A函数的调用,那么有没有办法在B函数体内部,完成正常的A函数调用流程,这样会对函数参数以及返回值的检查或者统计会有很大帮助。
非常感谢!
执行go test ./... -gcflags=all=-l
报错,代码拉取的是v2.1.0版本
test/dsl_test/func_dsl_test.go:20:10: undefined: Belong
test/dsl_test/func_dsl_test.go:27:12: undefined: Belong
test/dsl_test/func_dsl_test.go:37:4: undefined: Belong
? github.com/agiledragon/gomonkey/v2 [no test files]
? github.com/agiledragon/gomonkey/v2/dsl [no test files]
ok github.com/agiledragon/gomonkey/v2/test (cached)
FAIL github.com/agiledragon/gomonkey/v2/test/dsl_test [build failed]
? github.com/agiledragon/gomonkey/v2/test/fake [no test files]
FAIL
大佬,我在给新人安利使用gomonkey进行单元测试的时候,我让他们到test文件夹下的官方范例中进行学习。
但是新人反应,gomonkey的官方教程中使用到了convey,但是他们并不了解convey,convey包给他们阅读代码带来了较大的不便性,也不太愿意再花费时间再去花时间学习convey,甚至有些新人误以为convey和gomonkey是耦合的。
大佬您看能不能把test包下的测试范例修改成不含有convey包的形式,可以降低学习的成本,方便新人入门。
import (
"reflect"
"testing"
"github.com/agiledragon/gomonkey"
"github.com/gin-gonic/gin"
"github.com/golang/mock/gomock"
)
func TestSeq_GIN(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ginCtx := &gin.Context{
Params: gin.Params{
gin.Param{
Key: "ns",
Value: "aaaa",
},
},
}
patches := gomonkey.ApplyMethod(reflect.TypeOf(ginCtx), "Param", func(_ *gin.Context, _ string) string {
return "bbbb"
})
defer patches.Reset()
println("===1", ginCtx.Param("ns"))
t.Log("===2", ginCtx.Param("ns"))
}
go test -v 输出结果
test/apply_interface_reused_test.go第20行应该是 db := fake.NewDb("etcd"),而不是db := fake.NewDb("mysql"),请大神检查一下
test failed in TestApplyFuncSeq, TestApplyInterfaceReused, TestApplyMethodSeq.
And they seems all related to reflect.Makefunc
.
reproduce commands:
cd test;
go test -v -gcflags=all="-N -l"
gomonkey 是否被多协程并发用于同一个目标的打桩?如果是,则需要将之前的协程先优雅退出;
我A函数goroutine并发调用B函数,B函数中的调用proto.Unmarshal,发现对proto.Unmarshal ApplyFucn 为生效
Could we mock interface variable directly instead of create a struct implementing interface methods?
If technically we can, how difficult is it? What should we do ?
I think we can dynamiclly create a struct and add methods to it, and use this struct as an instance of type of interface implemention.
panic: permission denied [recovered]
panic: permission denied
goroutine 13 [running]:
testing.tRunner.func1(0xc4202081e0)
/.../goroot/go/src/testing/testing.go:742 +0x29d
panic(0x4e697e0, 0xc4205a7c18)
/.../goroot/go/src/runtime/panic.go:502 +0x229
github.com/agiledragon/gomonkey.modifyBinary(0x4bf4570, 0xc420229a7c, 0xc, 0xc)
/.../gopath/src/github.com/agiledragon/gomonkey/modify_binary_darwin.go:11 +0x198
github.com/agiledragon/gomonkey.replace(0x4bf4570, 0x5099668, 0xc420229b58, 0x59aa580, 0x4dd7500)
/.../gopath/src/github.com/agiledragon/gomonkey/patch.go:164 +0x112
github.com/agiledragon/gomonkey.(*Patches).applyCore(0xc420086c40, 0x4dd7560, 0x5099ba8, 0x13, 0x4dd7560, 0x5099668, 0x13, 0x10)
/.../gopath/src/github.com/agiledragon/gomonkey/patch.go:140 +0x161
github.com/agiledragon/gomonkey.(*Patches).ApplyFunc(0xc420086c40, 0x4dd7560, 0x5099ba8, 0x4dd7560, 0x5099668, 0x4009d7d)
/.../gopath/src/github.com/agiledragon/gomonkey/patch.go:60 +0xbf
github.com/agiledragon/gomonkey.ApplyFunc(0x4dd7560, 0x5099ba8, 0x4dd7560, 0x5099668, 0x2ae)
/.../gopath/src/github.com/agiledragon/gomonkey/patch.go:22 +0xa2
the method
import (
"fmt"
remote "github.com/shima-park/agollo/viper-remote"
"github.com/spf13/viper"
)
var configType = "prop"
func fetchApollo(ip, namespace string, conf *common.Config) error {
v := viper.New()
v.SetConfigType(configType)
err := v.AddRemoteProvider("apollo", ip, namespace)
if err != nil {
log.Error(fmt.Sprintf("AddRemoteProvider err : %s", err.Error()))
return err
}
err = v.ReadRemoteConfig()
if err != nil {
log.Error(fmt.Sprintf("ReadRemoteConfig err : %s", err.Error()))
return err
}
err = v.Unmarshal(conf)
if err != nil {
log.Error(fmt.Sprintf("unmarshal Config err : %s", err.Error()))
return err
}
return nil
}
the test case
import (
"errors"
"testing"
"github.com/agiledragon/gomonkey"
. "github.com/smartystreets/goconvey/convey"
"github.com/spf13/viper"
)
func Test_fetchApollo(t *testing.T) {
var viperpt *viper.Viper
var conf = &common.Config{}
Convey(casenameprefix, t, func() {
Convey(casenameprefix+"error[fetchApollo][Unmarshal]", func() {
patch := gomonkey.ApplyMethod(reflect.TypeOf(viperpt), "AddRemoteProvider", func(_ *viper.Viper, provider, endpoint, path string) error {
log.Debug("ApplyMethod viper.Viper.AddRemoteProvider success")
return nil
})
patch.ApplyMethod(reflect.TypeOf(viperpt), "ReadRemoteConfig", func(_ *viper.Viper) error {
log.Debug("ApplyMethod viper.Viper.ReadRemoteConfig success")
return nil
})
patch.ApplyMethod(reflect.TypeOf(viperpt), "Unmarshal", func(_ *viper.Viper, rawVal interface{}, opts ...viper.DecoderConfigOption) error {
log.Debug("ApplyMethod viper.Viper.Unmarshal success")
return errors.New("custom errors")
})
defer patch.Reset()
fetchApollo(conf)
})
})
}
the result
=== RUN Test_fetchApollo
custom:
custom:error[fetchApollo][Unmarshal] {"T":"2020-11-06T09:16:20.694+0800","C":"config/agollo_test.go:69","L":"debug","timestamp":1604625380,"M":"ApplyMethod viper.Viper.AddRemoteProvider success"}
{"T":"2020-11-06T09:16:20.697+0800","C":"config/agollo.go:51","L":"error","timestamp":1604625380,"M":"ReadRemoteConfig err : Remote Configurations Error: No Files Found"}
{"T":"2020-11-06T09:16:20.698+0800","C":"config/agollo.go:26","L":"error","timestamp":1604625380,"M":"get fetchApollo err : Remote Configurations Error: No Files Found"}
0 total assertions
--- PASS: Test_fetchApollo (0.01s)
PASS
ok config 1.537s
从运行结果来看,只有第一个 method AddRemoteProvider
patch成功了,后面的两个方法都没patch上,如果不patch AddRemoteProvider
,将测试方法改为如下
import (
"errors"
"testing"
"github.com/agiledragon/gomonkey"
. "github.com/smartystreets/goconvey/convey"
"github.com/spf13/viper"
)
func Test_fetchApollo(t *testing.T) {
var viperpt *viper.Viper
var conf = &common.Config{}
Convey(casenameprefix, t, func() {
Convey(casenameprefix+"error[fetchApollo][Unmarshal]", func() {
//patch := gomonkey.ApplyMethod(reflect.TypeOf(viperpt), "AddRemoteProvider", func(_ *viper.Viper, provider, endpoint, path string) error {
// log.Debug("ApplyMethod viper.Viper.AddRemoteProvider success")
// return nil
//})
patch := gomonkey.ApplyMethod(reflect.TypeOf(viperpt), "ReadRemoteConfig", func(_ *viper.Viper) error {
log.Debug("ApplyMethod viper.Viper.ReadRemoteConfig success")
return nil
})
patch.ApplyMethod(reflect.TypeOf(viperpt), "Unmarshal", func(_ *viper.Viper, rawVal interface{}, opts ...viper.DecoderConfigOption) error {
log.Debug("ApplyMethod viper.Viper.Unmarshal success")
return errors.New("custom errors")
})
defer patch.Reset()
fetchApollo(conf)
})
})
}
=== RUN Test_fetchApollo
custom:
custom:error[fetchApollo][Unmarshal] {"T":"2020-11-06T09:24:01.800+0800","C":"config/agollo.go:51","L":"error","timestamp":1604625841,"M":"ReadRemoteConfig err : Remote Configurations Error: No Files Found"}
{"T":"2020-11-06T09:24:01.801+0800","C":"config/agollo.go:26","L":"error","timestamp":1604625841,"M":"get fetchApollo err : Remote Configurations Error: No Files Found"}
0 total assertions
--- PASS: Test_fetchApollo (0.00s)
PASS
ok config 1.300s
从结果来看 ReadRemoteConfig
也没有patch成功
想知道这个是什么原因引起的,看了 github.com/spf13/viper
包中的两个方法,没看出有什么不同
What is the difference between ApplyMethodSeq and ApplyFuncSeq?
run go test on windows will output:
undefined: syscall.PROT_READ
//test/func_test.go
package test
import (
. "github.com/agiledragon/gomonkey"
. "github.com/smartystreets/goconvey/convey"
"testing"
)
func TestGetGroupLastSend(t *testing.T) {
Convey("Comparing two variables", t, func() {
num := 333
ApplyFunc(GetB, func() int {
return num
})
result := GetA()
So(num, ShouldEqual, result)
})
}
//test/func.go
package test
func GetA() int {
return GetB()
}
func GetB() int {
return 1
}
Expected: '1'
Actual: '333'
(Should be equal)
I am not sure why this is failed. Did I use it in the wrong way?
如题,光看test有点看不懂
apply的时候生成的中间函数是临时变量,过程中遇到gc之后,再调用测试函数会出现panic
v2.1.0 is not available on https://pkg.go.dev/github.com/agiledragon/gomonkey?tab=versions
persion.go 代码如下
package model
import (
"strings"
)
/*
@Author: zhijian
@Date: 2021/3/29 18:54
@Description:
*/
//单例变量
var One = &Persion{Name: "test"}
//类型为公开的没有任何问题
//可是如果这里为私有 persion 那么 ApplyMethod 最后一个参数 double interface{} 因为不在同一个包里面 就会取不到方法的 receiver 的类型
//但是为私有 persion 的时候 ApplyMethodSeq 因为不需要显式的填这个取不到的类型 编译可以通过,可以正常使用
//这个地方有比较优雅的解决方案吗?还是我使用的姿势不对?
type Persion struct {
Name string
}
func (p *Persion) Echo(age int) (string,error) {
var sb strings.Builder
sb.WriteString(p.Name)
sb.WriteString(string(rune(age)))
return sb.String(), nil
}
func (p *Persion) EchoMaybeDiff(age int) (string,error) {
var sb strings.Builder
sb.WriteString(p.Name)
sb.WriteString(string(rune(age)))
return sb.String(), nil
}
svc.go 代码如下
package svc
import "dtstack.com/dtstack/easymatrix/matrix/mock/model"
/*
@Author: zhijian
@Date: 2021/3/29 19:08
@Description:
*/
func BizEcho() string {
echo, err := model.One.Echo(3)
if err != nil {
panic(err)
}
return echo
}
func BizEchoMaybeDiff() string {
echo, err := model.One.EchoMaybeDiff(3)
if err != nil {
panic(err)
}
return echo
}
svc_test.go 代码如下
package svc
import (
"dtstack.com/dtstack/easymatrix/matrix/mock/model"
"fmt"
. "github.com/agiledragon/gomonkey"
. "github.com/smartystreets/goconvey/convey"
"reflect"
"testing"
)
/*
@Author: zhijian
@Date: 2021/3/29 19:18
@Description:
*/
func TestEchoMaybeDiff(t *testing.T) {
Convey("test",t,
func() {
outputs := []OutputCell{
{Values: Params{`test1`, nil}},// 模拟函数的第1次输出
{Values: Params{`test2`, nil}},// 模拟函数的第2次输出
{Values: Params{`test3`, nil}},// 模拟函数的第3次输出
}
patches := ApplyMethodSeq(reflect.TypeOf(model.One), "EchoMaybeDiff", outputs)
defer patches.Reset()
echo1 := BizEchoMaybeDiff()
echo2 :=BizEchoMaybeDiff()
echo3 :=BizEchoMaybeDiff()
fmt.Println(echo1)
fmt.Println(echo2)
fmt.Println(echo3)
ShouldNotBeNil(echo3)
})
}
func TestBizEcho(t *testing.T) {
Convey("test",t,
func() {
patches := ApplyMethod(reflect.TypeOf(model.One), "Echo", func(_ *model.Persion, i int) (string, error) {
return "test", nil
})
defer patches.Reset()
bizEcho := BizEcho()
fmt.Println(bizEcho)
ShouldEqual(bizEcho, "test")
})
}
我觉得这种单例模式写法应该挺常见的,是不是我使用的姿势不对?
When I used gomonkey write test case in win10 platform, mocked os.RemoveAll() and it worked, but in Ubuntu14.04 and Ubuntu16.04, not worked. Please take a look at this case, see if it is a bug.
below is my test code and screenshot of test results.
package test
import (
"fmt"
"os"
"testing"
"github.com/agiledragon/gomonkey"
)
func Test_RemoveAll(t *testing.T) {
patch := gomonkey.NewPatches()
patch.ApplyFunc(os.RemoveAll, func(_ string) error {
fmt.Println("use mock functions to do rm -rf")
return nil
})
defer patch.Reset()
doRemove("")
}
func doRemove(path string) {
if err := os.RemoveAll(path); err != nil {
fmt.Println(err)
}
}
应该换成: -gcflags "all=-N -l"
比如被测函数为a,在a中依次调用了2次函数b,分别对这两个函数b打桩吗?
有个函数需要调用 oliver 库创建 elasticsearch client,使用下面方法
patches := gomonkey.ApplyFunc(elastic.NewClient,
func(options ...elastic.ClientOptionFunc) (*elastic.Client, error) {
t.Log("client MOCK >>>>>>>>>>>>>")
return nil, fmt.Errorf("someerr")
})
defer patches.Reset()
执行时,发现打印并没有出现。
关于源码有一个疑惑,希望可以得到解答,谢谢。
在patch.go的ApplyCore方法里,有这么一行
original := replace(*(*uintptr)(getPointer(target)), uintptr(getPointer(double)))
既然target和double都是函数,为什么两者取函数入口地址的方式不同,即为什么一个是*(uintptr)(getPointer(target),而另一个是uintptr(getPointer(double),请问(*uintptr)和uintptr的区别是什么?
Thanks sincerely for constantly using and supporting gomonkey, I will try my best to make gomonkey better, and keep growing gomonkey community. To attract more developers to use and contribute to gomonkey, please comment in this issue and include the following information:
Your company, school or organization.
Homepage.
You can refer to the following sample answer for the format:
Company: XXX
Home page: https://www.xxx.com
非常感谢每一位持续关注并使用 gomonkey 的朋友。我会持续投入,尽力把 gomonkey 变得更好,努力让 gomonkey 社区更加繁荣。为了更好的聆听社区的声音,吸引更多的人使用和参与,我期待您在此提交一条评论, 评论内容包括:
您所在公司、学校或组织
官方网站
您可以参考下面的样例来提供您的信息:
公司:XXX
主页:https://www.xxx.com
再次感谢你的参与!
Please add writer's name in license txt.
hello, I think the gomonkey is so cool, But in reality, I need Gomonkey to support the ARM64 system, so could you give me some suggestions on how I can adapt it? thanks.
I used it om arm platform,and it failed with such error: "../../../../pkg/mod/github.com/agiledragon/[email protected]+incompatible/patch.go:160:10: undefined: buildJmpDirective",but it runs ok on x86 platform.Can't it used on arm platform?
I have implemented monkey_arm64.go jmpToFunctionValue. Now trying to compile and execute my code on a Mac M1.
I am getting an exception when making a call to syscall.Mprotect (syscall.PROT_WRITE). Do you know how to fix this problem?
func mprotectCrossPage(addr uintptr, length int, prot int) {
pageSize := syscall.Getpagesize()
for p := pageStart(addr); p < addr+uintptr(length); p += uintptr(pageSize) {
page := rawMemoryAccess(p, pageSize)
err := syscall.Mprotect(page, prot) //syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC
if err != nil {
panic(err)
}
}
}
func jmpToFunctionValue(to uintptr) []byte {
/*return []byte{
0x48, 0xBA,
byte(to),
byte(to >> 8),
byte(to >> 16),
byte(to >> 24),
byte(to >> 32),
byte(to >> 40),
byte(to >> 48),
byte(to >> 56), // movabs rdx,to
0xFF, 0x22, // jmp QWORD PTR [rdx]
}*/
raw := []uint32{
uint32((((to) & 0xffff) << 5) | 0xD2800000), // MOVZ X0, <bytes 0,1>
uint32((((to >> 16) & 0xffff) << 5) | 0xF2A00000), // MOVK X0, <bytes 2,4>, LSL 16
uint32((((to >> 32) & 0xffff) << 5) | 0xF2C00000), // MOVK X0, <bytes 4,5>, LSL 32
uint32((((to >> 48) & 0xffff) << 5) | 0xF2E00000), // MOVK X0, <bytes 6,7>, LSL 48
0xD63F0000, // BLR X0
}
// Get the slice header
header := *(*reflect.SliceHeader)(unsafe.Pointer(&raw))
header.Len *= 4 // 4 bytes in uint32
header.Cap *= 4 // 4 bytes in uint32
// Convert slice header to an []byte
data := *(*[]byte)(unsafe.Pointer(&header))
return data
}
my system is mac m1
when I used gomonkey, it reports:
../../../../pkg/mod/github.com/agiledragon/[email protected]+incompatible/patch.go:160:10: undefined: buildJmpDirective
can you help to solve this issue?
求解
有没有快速生成mock 代码的方法,每次都ctrl c +v 很累,大部分代码完全可由原函数生成
target type(
func(*serverless.realServerlessControl, context.Context, string) error)
and double type(
func(*serverless.ServerlessControlInterface, context.Context, string) error) are different
其中 realServerlessControl实现了ServerlessControlInterface 这个interface,这种如何处理,望回复 感谢
type realServerlessControl struct {
}
type ServerlessControlInterface interface {
CreateFunction(ctx context.Context, functionId string) error
}
go 1.11.4能跑通的单元测试代码,在go1.14中跑不通
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.