優雅的使用Go進行單元測試

Go 單元測試

  • ????Go 單元測試git

  • ????1.單測工具github

  • ????2.單測golang

    • ????2.1 調本身框架

    • ????2.2 直接mock遠程調用接口ide

    • ????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裏面纔去賦值:code

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,具體安裝見前面。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

相關文章
相關標籤/搜索