Giter Club home page Giter Club logo

gomonkey's People

Contributors

abner-chenc avatar agiledragon avatar avolili avatar bbrodriges avatar dushaoshuai avatar for-acgn avatar myzhan avatar ryan--lee avatar taoso avatar testpersonal avatar trungdlp-wolffun avatar wathenjiang avatar wiselike avatar xieyanker avatar zhangguanzhang avatar zhongyuanjia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gomonkey's Issues

Wanted: who's using gomonkey? / 您在使用 gomonkey 吗?

Who is using gomonkey?

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吗?(本issue仅收集用户信息,不讨论具体问题)

非常感谢每一位持续关注并使用 gomonkey 的朋友。我会持续投入,尽力把 gomonkey 变得更好,努力让 gomonkey 社区更加繁荣。为了更好的聆听社区的声音,吸引更多的人使用和参与,我期待您在此提交一条评论, 评论内容包括:

您所在公司、学校或组织
官方网站
您可以参考下面的样例来提供您的信息:

公司:XXX
主页:https://www.xxx.com

再次感谢你的参与!

ApplyMethod有时生效,有时不生效

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 包中的两个方法,没看出有什么不同

macOS 10.15 syscall.Mprotect panic: permission denied

Hi:
After I was update my macOS to about 10.14, It will report panic: permission denied when I run ApplyFunc or ApplyMethod. I searched this problem in google, but I don't have answer. Which permission I should set?
Thanks a lot.
This is panic stack.

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

go test build fail

执行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

对接口方法mock报错 call of reflect.flag.mustBeExported on zero Value

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

could we mock interface directly?

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.

ApplyMethod mock的目标是个interface,实际的函数调用是个struct,mock的时候报target type and double type are different

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
}

panic: permission denied

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
}

Quesion of calling original function

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函数调用流程,这样会对函数参数以及返回值的检查或者统计会有很大帮助。
非常感谢!

无法mock http.Client的Do方法

您好,在业务中需要对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()

v2-dsl: "panic: patch has been existed [recovered]"

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?

it seems not worked in linux OS when I try to mock functions from os package

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)
	}
}
  • win10 result
    image

  • ubuntu14.04 result
    image

arm64 test failed in some TestFunctions

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"

单例模式无法进行成员方法打桩

我写了一个小 demo 模拟这个问题
首先目录结构如下图
image

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")
		})
}

我觉得这种单例模式写法应该挺常见的,是不是我使用的姿势不对?

ApplyMethod mock 非导出成员方法报 “retrieve method by name failed”

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
}

能否再简化gomonkey的测试范例,不要使用convey包

大佬,我在给新人安利使用gomonkey进行单元测试的时候,我让他们到test文件夹下的官方范例中进行学习。
但是新人反应,gomonkey的官方教程中使用到了convey,但是他们并不了解convey,convey包给他们阅读代码带来了较大的不便性,也不太愿意再花费时间再去花时间学习convey,甚至有些新人误以为convey和gomonkey是耦合的。
大佬您看能不能把test包下的测试范例修改成不含有convey包的形式,可以降低学习的成本,方便新人入门。

关于取函数入口地址的问题

关于源码有一个疑惑,希望可以得到解答,谢谢。
在patch.go的ApplyCore方法里,有这么一行
original := replace(*(*uintptr)(getPointer(target)), uintptr(getPointer(double)))
既然target和double都是函数,为什么两者取函数入口地址的方式不同,即为什么一个是*(uintptr)(getPointer(target),而另一个是uintptr(getPointer(double),请问(*uintptr)和uintptr的区别是什么?

并发不安全范围

如下代码,并发访问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())
}

ApplyMethod Invalid with -gcflags=all=-l in beego framework controller.ServeJSON()

my environment:

  • mac Os 10.14.6
  • golang 1.14.2
  • gomonkey 2.0.2
  • beego framework

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 not effect, but effected in debug mode

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

方法体内的执行代码太少的时后不会生效

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没被替换

为elastic.NewClient打桩失败

有个函数需要调用 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()

执行时,发现打印并没有出现。

can gomonkey used on arm platform?

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?

apply_interface_reused_test.go 跑起来后mock失败

代码是这样的,单测失败了,应该是没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

mock function affect other tests after Patches.Reset()

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)
})

使用ApplyMethod替换gin.Context的方法未生效,为何?

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 输出结果

image

goroutine 进行mock的方案求解

在开发的业务场景中,有时候会遇到中间有一个函数中有一个协程,类似下面这种

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不能用于并发,但还是想请问这种情况有什么好的解决方案吗?多谢啦

ApplyFunc not working

//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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.