1. 程式人生 > >golang單元測試之mock

golang單元測試之mock

搞單元測試,如果碰到這些情況:
1,一個函式,內部包含了很多並且很深的呼叫,但是如果單單測這個函式,其實實現的功能很簡單。
2,一個函式,包含了其他還未實現的呼叫。
3,函式內部對資料的要求極為苛刻。

那麼這時候就可以考慮使用mock來處理。

mock,簡而言之就是可以通過注入我們所期望返回的資料,或者我們所期望傳遞的引數,來避免上面那些情況,其原理則是通過反射來實現。

這次就來看看golang的mock,gomock
gomock是go官方提供的mock解決方案,主要分為兩部分:gomock庫和mock程式碼生成工具mockgen。

使用舉例:

package metal

type
Imetal interface { GetName() string SetName(string) string } type Metal struct { Name string Exchange string } func (self Metal) GetName() string { if self.Name==""{ return "none" } return self.Name } func (self *Metal) SetName(brand string) string { self.
Name=brand return "done" }

我現在有一個package,其包含了IMetal介面,這個介面下面有兩個方法,現在針對這兩個方法來進行mock,ps:gomock只支援interface方法的mock。

在mock之前,需要先通過mockgen來生成mock程式碼,我的源就是上面的IMeta介面。

簡單介紹一下mockgen:
它有兩種工作模式---source和reflect

source模式
mockgen -source=foo.go [other options]
根據原始檔來生成,原始檔是包含了一個或多個interface的檔案。

reflect模式
mockgen src/package Conn,Driver
一個檔案定義了多個interface而你只想對部分interface進行mock,或者interface存在巢狀,使用reflect模式

mock程式碼生成好之後,接下來是寫測試函式。

package metal

import (
    "mock_metal"
    "github.com/golang/mock/gomock"
    "testing"
    "fmt"
)

func GetMetalName(mi Imetal) string {
    mi.GetName()
    return mi.GetName()
}

func SetMetalName(mi Imetal,name string) string {
     return  mi.SetName(name)
}


func TestMetalName(t *testing.T)  {
    mockCtl := gomock.NewController(t)
    defer mockCtl.Finish()
    mockMetal := mock_metal.NewMockImetal(mockCtl)   //mock_metal就是生成的mock程式碼,以包的形式存在

    m:=new(Metal)
    mockCtl.RecordCall(m,"GetName").Times(1)
    mockCtl.Call(m,"GetName")

    call:=mockMetal.EXPECT().GetName().Return("apple")
    mockMetal.EXPECT().GetName().Return("peer").After(call)   //注入期望的返回值

    mockedBrand:=GetMetalName(mockMetal)

    mockMetal.EXPECT().SetName(gomock.Eq("al")).Do(func(format string) {   //入參校驗
     fmt.Println("recv param :",format)
    }).Return("setdone")

    mockMetal.EXPECT().SetName(gomock.Any()).Do(func(format string) {    //入參不做校驗
        fmt.Println("recv param :",format)
    }).Return("setdone")

    mockedSetName:=SetMetalName(mockMetal,"al")
    fmt.Println(mockedSetName)

    if "peer"!=mockedBrand{
        t.Error("Get wrong name:", mockedBrand)
    }

    if "setdone"!=mockedSetName{
        t.Error("Set wrong name:", mockedSetName)
    }
}

然後執行go test即可,會發現這些被mock的函式會按照我們定義的行為來執行。

附帶兩個gomock的官方資料,基本上看完了就可以上手了。

第一個是gomock庫的所有方法說明,第二個是官方的例子,裡面有如何進行gomock的方法使用。