首页 文章 Go语言基础 优雅的使用Go进行单元测试
0
0
0
24

优雅的使用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,具体安装见前面。

到此这篇关于“优雅的使用Go进行单元测试”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持Go语言编程网!

相关文章

创建博客

开始创作
写作能提升自己能力,也能为他人分享知识。

在线教程

查看更多
  • Go入门指南

    Go入门指南

  • Go语言高级编程

    Go语言高级编程

  • Go Web 编程

    Go Web 编程

  • GO专家编程

    GO专家编程

  • Go语言四十二章经

    Go语言四十二章经

  • 数据结构和算法(Golang实现)

    数据结构和算法(Golang实现)

Go语言编程网

微信扫码关注订阅号


博客 资讯 教程 我的