測試單個文件,必定要帶上被測試的原文件git
go test -v wechat_test.go wechat.gogithub
測試單個方法golang
go test -v -test.run TestRefreshAccessToken數據庫
go 單元測試寫法
參考 https://studygolang.com/articles/22982安全
testing
go語言package提供的自動化測試框架,
testing支持普通用例測試、壓力測試、並行測試,基本能知足單測需求;併發
缺失:框架
testing不支持mock數據,想要mock數據必須本身實現mock邏輯,這會增長許多沒必要要的工做量。ide
mock的重要性
理想狀況下,一個好的函數結構必須有入參、出參,完成的是一項單1、獨立的工做,好比:函數
func add(a int, b int) int {
return a + b
}
這樣的函數寫單測很簡單,且輕易就能達到100%覆蓋率。單元測試
但實際工做中,這樣純粹的函數佔比可能不到10%,單個函數每每不是獨立的,它須要依賴於其餘模塊、三方庫、數據庫等等。
以上傳圖片爲例,上傳一張圖片樣本函數:
func uploadImage(content []byte, UID string, fdfs *tsFDFS) error {
// 1. 上傳圖片到fdfs
url := fdfs.Upload(content)
// 2. 生成縮略圖
resize.Resize(…)
jpeg.Encode(…)
// 3. 圖片入庫
sample := &database.yangbenModel{ … }
database.CreateSample(…)
// 4. 圖片綁定任務
binding := &database.ExamplennotationTask{ … }
database.CreateBinding(…)
return nil
}
若是在單測中想要正常走完這個函數且不出錯,咱們須要有一個真實的fdfs環境能夠上傳數據成功,一個真實的數據庫,且根據業務邏輯,咱們想要插入一個sample前,必須有對應的enterprise、pipeline、annotation-task等等;
因而,在此前,項目中使用testing的單測實現思路是這樣的:
func TestUploadImage(t *testing.T) {
// 1. 建立真實的fdfs環境
fdfs := &FDFS{ … }
// 2. 鏈接真實的數據庫
db := database.NewGormConn(…)
// 3. 插入一條enters數據
enters := &database.EnterpriseModel{…}
database.CreateEnters(…)
// 4. 插入一條業務數據
…
// 5. 插入另一條業務數據
…
// 6. 開始執行單測
uploadImage(…)
…
}
能夠看到,單測以前進行了大量耗時的、冗餘的、無心義的工做
單測執行耗時長,正常一條單測時間通常在100ms內,在模擬了真實場景後,單測時間大幅增長,甚至項目中有部分單測超過1分鐘;
單測編碼效率低,須要瞭解了業務場景後,才能編寫完一條單測case;
過於依賴真實環境,穩定性差,維護成本高,對於一些沒法模擬真實環境的流程,只能放棄覆蓋測試。
mock框架
gomonkey 單元測試
安裝
go get -u -v https://github.com/agiledragon/gomonkey
github地址爲:https://github.com/agiledragon/gomonkey
參考博客: https://studygolang.com/articles/15034
目前本身嘗試過mock方法 把代碼使用的粘貼供gopher參考下
這個地方是rpc調用其餘服務,
測試中用打樁的方式,將這個
函數的結果先執行了,當執行
這個函數時,採用這個函數
打樁時mock的數據就能夠了
測試文件中如何寫mock第三方數據呢?
ApplyMethod 第三個參數
func() 中的第一個參數爲這個
方法屬於那個類型,剩餘的參數
書寫方式爲 func(_ 綁定的那個類型,
,_ type)
例如
func(_ 綁定的那個類型,
,_ string, _ int)(返回結果)
gostub
對全局變量、函數或過程打樁,對代碼中的外部依賴進行劫持並替換成mock的內容
爲一個全局變量打樁
stubs := gostub.Stub(&num, 10)
defer stubs.Reset()
爲一個函數打樁
stubs := gostub.StubFunc(&Func, func(args string) error {
return nil
})
defer stubs.Reset()
爲一個過程打樁
stubs := gostub.StubFunc(&Destroy)
defer stubs.Reset()
場景組合
以上文測試上傳一張圖片樣本爲例
func TestUploadImage(t *testing.T) {
// 1. stub fdfs
stubs := gostub.StubFunc(&Upload, func() string {
return 「」
})
defer stubs.Reset()
// 2. stub 生成縮略圖 stubs = gostub.StubFunc(&resize.Resize, func() error { return nil }) // 3. stub sample create stubs = gostub.StubFunc(&database.CreateSample, func() error { return nil }) ... // 4. stub binding create ... // 5. 開始執行單測 uploadImage(...) ...
}
缺點
對代碼有必定的侵入性 (須要將函數改成全局變量語法形式、對三方庫須要作一層包裝)
沒法對方法(成員函數)進行打樁
monkey
monkey是go的一個猴子補丁(monkeypatching)框架,在運行時經過彙編語句重寫可執行文件,將待打樁函數或方法的實現跳轉到樁實現,原理和熱補丁相似。
經過monkey能夠解決stub沒法爲方法打樁的問題,但monkey不是線程安全的,不能用於併發測試。
爲一個函數打樁
guard := monkey.Patch(Func, func(args string) error {
return nil
})
defer guard.Unpatch()
爲一個方法打樁
guard := monkey.PatchInstanceMethod(reflect.TypeOf(client), 「Test」, func(args string) error {
return nil
})
defer guard.Unpatch()缺點線程不安全在macOS 10.15及以上版本沒法運行,因爲其原理是修改可執行文件,從Catalina版本起,系統對文件讀寫權限作了更嚴格的管理,monkey修改的文件無寫權限,運行panic,參考https://github.com/agiledragon/gomonkey/issues/10