优雅的使用Go进行单元测试

Go 单元测试

  • ????Go 单元测试

  • ????1.单测工具

  • ????2.单测

    • ????2.1 调自己

    • ????2.2 直接mock远程调用接口

    • ????2.3 monkey

  • ????3.优雅的单测

1.单测工具

// go mock相关:
go get github.com/golang/mock/gomock
go get github.com/golang/mock/mockgen
//stub相关:
go get github.com/prashantv/gostub
// monkey
go get github.com/bouk/monkey
// goconvey
go get github.com/smartystreets/goconvey

2.单测

2.1 调自己

在单元测试过程中,遇到了两个问题,第一个:

func Target() {
    A()
}
func A() {
    // call rpc interface

}

现在我们想测试Target函数,但是由于调用的A函数依赖于自己的某个函数,这里就是A调用了rpc接口拉别人接口数据,我们想mockA接口的目标是,想直接拿到A返回的数据即可,直接采用gomock方式,行不通,自己测试了一下,发现要不断的mock 别人接口所依赖的其他接口,非常麻烦,通过注入代码或者后面第三种方式替换函数即可解决。

实践中只需要按照下面方法来,注入一个getHook函数,在test里面才去赋值:

var getHook func([]string) ([]Info, error)

func Target() {
    if getHook != nil {
        info, err = getHook("xxx")
    } else {
        info, err = handler.getInfo("xxx")
    }
}

其中返回的数据时Info:

type Info struct {
 ID string,
}

test文件中,这样写:

func mockGetInfo(x []string) ([]Info, error) {
    res := []Info{
        {
           ID: "xxx"
        },
    }
    return res, nil
}
func Test_xxx(t *testing.T) {
    // 注入屏蔽测试
    getHook = mockGetInfo // inject
    getHook("xxx")
}

这样,就可以把这个接口给mock掉了。

2.2 直接mock远程调用接口

在代码中,还会有调别人的服务,例如:双方约定Pb rpc协议来调用拉取数据,现有下面这个接口:

type Service interface {
    GetSerData(req *SerReq) (rsp *SerRsp, err error)
}

主函数调用如下

// 请求渲染后台
som := NewServiceClientProxy(opts...)
// 发起rpc调用
rsp, _ := som.GetSerData(&req)

这个就比较简单了,直接采用gomock+gostub即可解决,不需要注入代码及主逻辑,非常方便!

首先,使用mockgen生成相应mock_service.go

mockgen -destination=mocks/mock_service.go -package=mocks com.gcx Service

该命令中解释如下:

  • destination表示生成的目标文件

  • package表示上述文件的包名

  • com.gcx表示mock的接口包名

  • Service表示接口名

使用gostub对proxy进行打桩,可以简单理解位用自己的替换代码中想mock的接口。

ctrl := gomock.NewController(t)
mockedService := mocks.NewMockServiceClientProxy(ctrl)
serStubs := gostub.Stub(&NewServiceClientProxy, func(opts ...client.Option) Service {
    return mockedService
})
defer serStubs.Reset()

随后,我们想通过自己的mock自己想要的数据,只需要下面这样描述预期行为即可:

mockedService.EXPECT().GetSerData(gomock.Any(), gomock.Any(), gomock.Any()).Return(&SerRsp{
    // 填充字段
}, nil).AnyTimes()

2.3 monkey

使用monkey测试,算是最简单的一种方式了,不用自己去打桩,然后替换,也不用像方法1一样进行主逻辑的函数注入,mock谁,我们就替换掉这个方法或者函数就行了,而mockey就是这么直接的。

首先看一下安装问题:正常的 方式为:

import "github.com/bouk/monkey"

源码指定了 import 方式,因此实际单测中应该:

import "bou.ke/monkey"

此时,需要进入gopath里面:go/pkg/mod/github.com/bouk,重命名文件夹:mv github.com/bouk bou.ke

如何去使用呢,下面举个例子:

假设要测试getNum:

func getNum() {
   // dosomething
    handler := &ProcessHandler{}
    unionInfo, err = handler.GetSerData("xxx")
   // dosomething
}
// 我们想mock掉该接口,该接口具体实现如下:
func (s *ProcessHandler) GetSerData(c []string) ([], error) {
    // dosomething
    return info, err
}

此时我们直接使用

patch

进行替换即可:

var s *ProcessHandler
monkey.PatchInstanceMethod(reflect.TypeOf(s), "GetSerData", func(o *ProcessHandler, x []string) ([]union.CoverInfo, error) {
    res := []Info{
        {
           ID: "xxx"
        },
    }
    return res, nil
})

mockey是有一些坑的,例如:你要测是的函数或者方法不可导出,就会报下面错误:

这里以GetSerData不可导出为例:panic: unknown method GetSerData反射机制的这种差异导致了Monkey框架的缺陷:在go1.6版本中可以成功打桩的首字母小写的方法,当go版本升级后Monkey框架会显式触发panic,表示

unknown method:

具体patch的原理见后面参考。

3.优雅的单测

vscode生成的单测,如下:

func Test_getNum(t *testing.T) {

    tests := []struct {
        name    string
        args    args
        wantErr bool
    }{
        {
            name: "TestGetNum",
            args: args{
                req: &SerReq{
                },
            },
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if err := getNum(tt.args.req); (err != nil) != tt.wantErr {
                t.Errorf("getNum error = %v, wantErr %v", err, tt.wantErr)
            }
        })
    }
}

太挫了,来看一下高富帅的:

convey.Convey("getNum invoke test", t, func() {
    type args struct {  
        req *SerReq
    }
    input := args{
        ctx: ctx,
        req: &SerReq{
        
        },
    }
    err := getNum(req)
    convey.So(err, convey.ShouldBeNil)
})

这里引入convey,具体安装见前面。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值