Go單測系列5—使用monkey打樁


這是Go語言單元測試從零到溜系列教程的第4篇,介紹了如何在單元測試中使用monkey進行打樁。

在上一篇《Go單測系列5—mock接口測試》中,我們介紹了如何在單元測試中使用gomockgostub工具mock接口及打樁。

在這一篇中我們將介紹一個更強大的打樁工具——monkey,它支持為任意函數及方法進行打樁。

《Go單測從零到溜系列》的示例代碼已上傳至Github,點擊👉🏻https://github.com/go-quiz/golang-unit-test-demo 查看完整源代碼。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM