cch123 / supermonkey

Patch all Go functions for testing
MIT License
254 stars 17 forks source link

Can't get the returned value? #5

Closed jiahuat closed 4 years ago

jiahuat commented 4 years ago

If the mock function has a return value, how could I get it?

ioworker0 commented 4 years ago

If the mock function has a return value, how could I get it?

Wait a minute, I'll show you a demo.

ioworker0 commented 4 years ago

If the mock function has a return value, how could I get it?


import (
    "fmt"

    sm "github.com/cch123/supermonkey"
)

func patchFuncSymbol() {
    fmt.Println(heyHeyHey())

    var res string
    patchGuard := sm.PatchByFullSymbolName("main.heyHeyHey", func() string {
        res = func() string {
            // Do what you want ....
            return "Are you ok?"
        }()
        return res
    })

    fmt.Println(heyHeyHey())
    fmt.Println(res)
    patchGuard.Unpatch()
}

//go:noinline
func heyHeyHey() string {
    return "I'm not ok!"
}

I hope it will help you.

jiahuat commented 4 years ago

感谢你的回答! 实际情况比上面的栗子复杂点。我想mock一个grpc call。grpc方法大致如下: MyFunc(ctx context.Context, in SearchRequest, opts ...grpc.CallOption) (SearchResponse, error) 实现如下: patchGuard := sm.PatchByFullSymbolName("xxxxxxx.pb.path.(Client).MyFunc", func(ctx context.Context, in pb.SearchRequest, opts ...grpc.CallOption) (pb.SearchResponse, error) { // 注意⚠️这个返回值类型 result := &pb.SearchResponse{Data: some mock data} return result, nil }) 期望的是在涉及调用MyFunc的时候 都返回mock中的result, 实际上在运行中,获取到的SearchResponse都是nil; 奇怪的是 将PatchFunc中的 方法的返回值类型改为 interface{}, 成功获取到了期望中mock的result,改变后的PatchFunc签名如下: func(ctx context.Context, in pb.SearchRequest, opts ...grpc.CallOption) (interface{}, error)

按照supermock的设计,如果签名不对应该会报错的吧,目前这个现象有点迷惑

ioworker0 commented 4 years ago

感谢你的回答! 实际情况比上面的栗子复杂点。我想mock一个grpc call。grpc方法大致如下: MyFunc(ctx context.Context, in SearchRequest, opts ...grpc.CallOption) (SearchResponse, error) 实现如下: patchGuard := sm.PatchByFullSymbolName("xxxxxxx.pb.path.(Client).MyFunc", func(ctx context.Context, in pb.SearchRequest, opts ...grpc.CallOption) (pb.SearchResponse, error) { // 注意⚠️这个返回值类型 result := &pb.SearchResponse{Data: some mock data} return result, nil }) 期望的是在涉及调用MyFunc的时候 都返回mock中的result, 实际上在运行中,获取到的SearchResponse都是nil; 奇怪的是 将PatchFunc中的 方法的返回值类型改为 interface{}, 成功获取到了期望中mock的result,改变后的PatchFunc签名如下: func(ctx context.Context, in pb.SearchRequest, opts ...grpc.CallOption) (interface{}, error)

按照supermock的设计,如果签名不对应该会报错的吧,目前这个现象有点迷惑

方便上传到仓库看下这个demo吗?感觉你这种写法应该是没有问题的。

ioworker0 commented 4 years ago

这个涉及到公司项目 不方便上传,如果你那边方便的话 不知道是否可以加下微信好友,我们私下沟通:940311859

你尝试以下两种方式,不行的话再Call我哦。

1. Please add -l to your gcflags
> go run -gcflags="-l" yourfile.go

2. Add `//go:noinline` to func which you want to patch.
//go:noinline
func MyFunc(ctx context.Context, in *SearchRequest, opts ...interface{}) (*SearchResponse, error) {
ioworker0 commented 4 years ago

这个涉及到公司项目 不方便上传,如果你那边方便的话 不知道是否可以加下微信好友,我们私下沟通:940311859

我使用跟你同一种方式去patch是ok的,应该就是代码内联的问题。

jiahuat commented 4 years ago
  1. 是跑的test :go test -ldflags="-s=false" -gcflags=-l
  2. 那个方法是grpc call, protoc-gen-go 自动生成的,如果在这个文件上面做修改的话 感觉不太好
ioworker0 commented 4 years ago
  1. 是跑的test :go test -ldflags="-s=false" -gcflags=-l
  2. 那个方法是grpc call, protoc-gen-go 自动生成的,如果在这个文件上面做修改的话 感觉不太好

麻烦你单独拎出来一个可测的demo吧,proto+test几个文件就好。

ioworker0 commented 4 years ago

这个涉及到公司项目 不方便上传,如果你那边方便的话 不知道是否可以加下微信好友,我们私下沟通:940311859

加你了,通过下

ioworker0 commented 4 years ago

When I tried it out, it seemed to work.

type SearchRequest struct {
    Req string
}

type SearchResponse struct {
    Resp string
}

type A struct {}

func (*A) MyFunc(ctx context.Context, in *SearchRequest, opts ...interface{}) (*SearchResponse, error) {
    return nil, nil
}

// Original func signature
func (*A) MyFunc(ctx context.Context, in *SearchRequest, opts ...interface{}) (*SearchResponse, error)

// PatchByFullSymbolName
func (_ *A, ctx context.Context, in *pb.SearchRequest, opts ...interface {}) (*pb.SearchResponse, error)
// or
func (_ uintptr, ctx context.Context, in *pb.SearchRequest, opts ...interface {}) (*pb.SearchResponse, error)

// code
sm.Patch((*pb.A).MyFunc, func(_ uintptr, ctx context.Context, in *pb.SearchRequest, opts ...interface{}) (*pb.SearchResponse, error) {
        return nil, nil
})

sm.PatchByFullSymbolName("pb.(*A).MyFunc", func(_ uintptr, ctx context.Context, in *pb.SearchRequest, opts ...interface{}) (*pb.SearchResponse, error) {
        return nil, nil
})
jiahuat commented 4 years ago

Yes, it worked, Thanks a lot!