Go 單測從零到溜系列—4- 使用 monkey 打樁
這是 Go 語言單元測試從零到溜系列教程的第 4 篇,介紹瞭如何在單元測試中使用 monkey 進行打樁。
在上一篇《Go 單測從零到溜系列 3—mock 接口測試》中,我們介紹瞭如何在單元測試中使用gomock
和gostub
工具 mock 接口及打樁。
在這一篇中我們將介紹一個更強大的打樁工具——monkey
,它支持爲任意函數及方法進行打樁。
monkey
介紹
monkey 是一個 Go 單元測試中十分常用的打樁工具,它在運行時通過彙編語言重寫可執行文件,將目標函數或方法的實現跳轉到樁實現,其原理類似於熱補丁。
monkey 庫很強大,但是使用時需注意以下事項:
-
monkey 不支持內聯函數,在測試的時候需要通過命令行參數
-gcflags=-l
關閉 Go 語言的內聯優化。 -
monkey 不是線程安全的,所以不要把它用到併發的單元測試中。
安裝
go get bou.ke/monkey
使用示例
假設你們公司中臺提供了一個用戶中心的庫varys
,使用這個庫可以很方便的根據 uid 獲取用戶相關信息。但是當你編寫代碼的時候這個庫還沒實現,或者這個庫要經過內網請求但你現在沒這能力,這個時候要爲MyFunc
編寫單元測試,就需要做一些 mock 工作。
// func.go
func MyFunc(uid int64)string{
u, err := varys.GetInfoByUID(uid)
if err != nil {
return "welcome"
}
// 這裏是一些邏輯代碼...
return fmt.Sprintf("hello %s\n", u.Name)
}
我們使用monkey
庫對varys.GetInfoByUID
進行打樁。
// func_test.go
func TestMyFunc(t *testing.T) {
// 對 varys.GetInfoByUID 進行打樁
// 無論傳入的uid是多少,都返回 &varys.UserInfo{Name: "liwenzhou"}, nil
monkey.Patch(varys.GetInfoByUID, func(int64)(*varys.UserInfo, error) {
return &varys.UserInfo{Name: "liwenzhou"}, nil
})
ret := MyFunc(123)
if !strings.Contains(ret, "liwenzhou"){
t.Fatal()
}
}
執行單元測試:
注意:這裏爲防止內聯優化添加了
-gcflags=-l
參數。
go test -run=TestMyFunc -v -gcflags=-l
輸出:
=== RUN TestMyFunc
--- PASS: TestMyFunc (0.00s)
PASS
ok monkey_demo 0.009s
除了對函數進行 mock 外monkey
也支持對方法進行 mock。
// method.go
type User struct {
Name string
Birthday string
}
// CalcAge 計算用戶年齡
func (u *User) CalcAge() int {
t, err := time.Parse("2006-01-02", u.Birthday)
if err != nil {
return -1
}
return int(time.Now().Sub(t).Hours()/24.0)/365
}
// GetInfo 獲取用戶相關信息
func (u *User) GetInfo()string{
age := u.CalcAge()
if age <= 0 {
return fmt.Sprintf("%s很神祕,我們還不瞭解ta。", u.Name)
}
return fmt.Sprintf("%s今年%d歲了,ta是我們的朋友。", u.Name, age)
}
如果我們爲GetInfo
編寫單元測試的時候CalcAge
方法的功能還未完成,這個時候我們可以使用 monkey 進行打樁。
// method_test.go
func TestUser_GetInfo(t *testing.T) {
var u = &User{
Name: "q1mi",
Birthday: "1990-12-20",
}
// 爲對象方法打樁
monkey.PatchInstanceMethod(reflect.TypeOf(u), "CalcAge", func(*User)int {
return 18
})
ret := u.GetInfo() // 內部調用u.CalcAge方法時會返回18
if !strings.Contains(ret, "朋友"){
t.Fatal()
}
}
執行單元測試:
❯ go test -run=User -v
=== RUN TestUser_GetInfo
--- PASS: TestUser_GetInfo (0.00s)
PASS
ok monkey_demo 0.012s
monkey
基本上能滿足我們在單元測試中打樁的任何需求。
社區中還有一個參考 monkey 庫實現的 gomonkey 庫,原理和使用過程基本相似,這裏就不再囉嗦了。除此之外社區裏還有一些其他打樁工具如 GoStub(上一篇介紹過爲全局變量打樁)等。
熟練使用各種打樁工具能夠讓我們更快速地編寫合格的單元測試,爲我們的軟件保駕護航。
總結
本文通過外部函數依賴及內部方法依賴兩個示例,介紹瞭如何使用monkey
對依賴的函數和方法進行打樁。
在下一篇中,我們將介紹編寫單元測試時常用的工具——goconvey
。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/DNyHG5xIXefJXhpVwB3R1A