使用?gomonkey?Mock?函數(shù)及方法示例詳解
前言
在 Golang 語(yǔ)言中,寫單元測(cè)試的時(shí)候,不可避免的會(huì)涉及到對(duì)其他函數(shù)及方法的 Mock,即在假設(shè)其他函數(shù)及方法響應(yīng)預(yù)期結(jié)果的同時(shí),校驗(yàn)被測(cè)函數(shù)的響應(yīng)是否符合預(yù)期。
其中,在 Mock 其他函數(shù)及方法的時(shí)候,我們常用到的一個(gè)測(cè)試類庫(kù)是「gomonkey」。特別地,對(duì)于方法和函數(shù)的 Mock,略有差異,在這里我們就分別給出函數(shù)和方法 Mock 示例,方便大家參考。
函數(shù)
在 Golang 語(yǔ)言中,函數(shù)是沒(méi)有接受者的方法,其形式為
func function_name([parameter list]) [return_types] {
函數(shù)體
}對(duì)于函數(shù)的 Mock 相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,假設(shè)我們對(duì) A 函數(shù)進(jìn)行單元測(cè)試,且 A 函數(shù)里面又調(diào)用了 B 函數(shù),例如
func A(ctx context.Context, str string) error {
if len(str) == 0 {
return errors.New("str is empty")
}
return test_package_name.B(ctx, str)
}為了將 A 函數(shù)的每一行代碼都覆蓋到,則其單元測(cè)試可以寫為:
func TestA(t *testing.T) {
type args struct {
ctx context.Context
str string
}
tests := []struct {
name string
args args
Setup func(t *testing.T)
wantErr error
}{
{
name: "len(str) == 0",
wantErr: errors.New("str is empty")
},
{
name: "正常響應(yīng)",
Setup: func(t *testing.T) {
patches := gomonkey.ApplyFunc(test_package_name.B, func(_ context.Context, _ string) error {
return nil
})
t.Cleanup(func() {
patches.Reset()
})
},
args: args{
ctx: context.Background(),
str: "test",
},
wantErr: nil,
},
}
// 執(zhí)行測(cè)試用例
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.Setup != nil {
tt.Setup(t)
}
err := A(tt.args.ctx, tt.args.str)
if err != nil {
assert.EqualError(t, err, tt.wantErr.Error(), "error 不符合預(yù)期")
}
})
}
}其中,ApplyFunc函數(shù)是用來(lái) Mock 函數(shù)的,其第一個(gè)參數(shù)為需要 Mock 的函數(shù)名稱(不需要寫參數(shù)列表),第二個(gè)參數(shù)為需要 Mock 的函數(shù)結(jié)果;特別地,在Setup里面,我們要記得顯式調(diào)用Cleanup對(duì)patches進(jìn)行Reset操作,防止該 Mock 影響其他測(cè)試用例。
方法
在 Golang 語(yǔ)言中,方法是含有接受者的函數(shù),其形式為
func (variable_name variable_data_type) function_name([parameter list]) [return_type]{
函數(shù)體
}對(duì)于方法的 Mock 相對(duì)來(lái)說(shuō)復(fù)雜一下,假設(shè)我們對(duì) A 函數(shù)進(jìn)行單元測(cè)試,且 A 函數(shù)里面又調(diào)用了結(jié)構(gòu) C 的 B 方法,例如
func A(ctx context.Context, str string) error {
if len(str) == 0 {
return errors.New("str is empty")
}
c := &test_package_name.C{}
return c.B(ctx, str)
}
為了將 A 函數(shù)的每一行代碼都覆蓋到,則其單元測(cè)試可以寫為:
func TestA(t *testing.T) {
// 初始化C結(jié)構(gòu)
var c *test_package_name.C
type args struct {
ctx context.Context
str string
}
tests := []struct {
name string
args args
Setup func(t *testing.T)
wantErr error
}{
{
name: "len(str) == 0",
wantErr: errors.New("str is empty")
},
{
name: "正常響應(yīng)",
Setup: func(t *testing.T) {
patches := gomonkey.ApplyMethod(reflect.TypeOf(c), "B", func(_ *test_package_name.C, _ context.Context, _ string) error {
return nil
})
t.Cleanup(func() {
patches.Reset()
})
},
args: args{
ctx: context.Background(),
str: "test",
},
wantErr: nil,
},
}
// 執(zhí)行測(cè)試用例
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.Setup != nil {
tt.Setup(t)
}
err := A(tt.args.ctx, tt.args.str)
if err != nil {
assert.EqualError(t, err, tt.wantErr.Error(), "error 不符合預(yù)期")
}
})
}
}
其中,ApplyMethod函數(shù)是用來(lái) Mock 方法的,其第一個(gè)參數(shù)為需要 Mock 的方法的接受者類型,第二個(gè)參數(shù)為需要 Mock 的方法名稱(字符串類型),第三個(gè)參數(shù)為需要 Mock 的方法的定義及 Mock 結(jié)果;特別地,第一個(gè)參數(shù)和第三個(gè)參數(shù)需要我們注意:
- 第一個(gè)參數(shù),需要使用
reflect.TypeOf獲取接受者的類型,初始化的接受者必須是真正的類型,如結(jié)構(gòu) C 組合了結(jié)構(gòu) D,而B方法是通過(guò)組合 D 得到的,則初始化的時(shí)候需要定義結(jié)構(gòu) D,而不是結(jié)構(gòu) C,否則會(huì)報(bào)空指針異常; - 第三個(gè)參數(shù),雖然
B方法的聲明是func(ctx context.Context, str string),但是在使用ApplyMethod的時(shí)候,需要將B方法的聲明修改為func(c *test_package_name.C, ctx context.Context, str string),即需要將方法的接受者置為方法的第一個(gè)參數(shù)。
參考
還有就是,大家在使用gomonkey的時(shí)候,有可能遇到權(quán)限校驗(yàn)的問(wèn)題以及非 Debug 模式運(yùn)行失敗的問(wèn)題,可以參考:
golang使用gomonkey和monkey來(lái)mock方法或者函數(shù)時(shí)報(bào)panic: permission denied
使用 gomonkey 遇到非 debug 模式執(zhí)行失敗的問(wèn)題及解決方法
到這里,本文就要結(jié)束了,希望對(duì)大家有所幫助。
到此這篇關(guān)于使用 gomonkey Mock 函數(shù)及方法的文章就介紹到這了,更多相關(guān)gomonkey Mock 函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang基礎(chǔ)學(xué)習(xí)之map的示例詳解
哈希表是常見(jiàn)的數(shù)據(jù)結(jié)構(gòu),有的語(yǔ)言會(huì)將哈希稱作字典或者映射,在Go中,哈希就是常見(jiàn)的數(shù)據(jù)類型map,本文就來(lái)聊聊Golang中map的相關(guān)知識(shí)吧2023-03-03
Golang實(shí)現(xiàn)簡(jiǎn)易的命令行功能
這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Golang實(shí)現(xiàn)一個(gè)簡(jiǎn)易的命令行功能,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解一下2023-02-02
golang 微服務(wù)之gRPC與Protobuf的使用
這篇文章主要介紹了golang 微服務(wù)之gRPC與Protobuf的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
Go?常見(jiàn)設(shè)計(jì)模式之單例模式詳解
單例模式是設(shè)計(jì)模式中最簡(jiǎn)單的一種模式,單例模式能夠確保無(wú)論對(duì)象被實(shí)例化多少次,全局都只有一個(gè)實(shí)例存在,在Go?語(yǔ)言有多種方式可以實(shí)現(xiàn)單例模式,所以我們今天就來(lái)一起學(xué)習(xí)下吧2023-07-07

