背景:上周技術三面被面試官問道golang中如何做測試和性能優化,一時間沒有反應過來, 不過還好沒挂。 特此記錄一貼,系統地講解 golang 中如何做測試,如何查找性能瓶頸。
測試的分類
說到測試,一般有兩種:單例測試和壓力測試。
所謂單例測試就是拿着一些測試用例依次去測試一下,強調的是通過與不通過的問題。
而壓力測試則是挑一些特别複雜,極端的測試用例多次重複地進行測試,強調地是性能上的問題。
golang 中通過 testing.T 和 testing.B 能夠很好的支援 單例測試和壓力測試。
寫一個簡單的被測試函數
package test
func Add(a,b int) int {
return a+b
}
寫一個單例測試函數
golang 的測試函數命名是有規範的,以Test打頭,後面緊跟被測函數名。如果在IDE中,當你寫完Test加被測函數的首字母時應該會有補全提示的。
package test
import (
"testing"
)
func TestAdd(t *testing.T) {
testCases := []struct{
a int
b int
c int
}{
{3,2,1 },
{10,5,5},
{ 1000000,1,999999},
}
for _, testCase := range testCases{
if result:=Add(testCase.b,testCase.c); result!=testCase.a{
t.Errorf("Expected get %d, but got: %d ", testCase.a, result)
}
}
}
當一個函數的參數為 testing.T時,同樣标志着這是一個單例測試函數。
随後我們可以直接在IDE 中點選函數旁邊的小三角進行運作, 也可以在指令行中輸入
go test -run TestAdd
這條指令會 指定測試 哪一個函數,如果想要測試目前包下的所有測試函數,也可以用下面這條指令
go test .
寫一個壓力測試函數
說完單例測試,該說壓力測試了。在剛剛的 TestAdd 下面寫一個 BenchmarkAdd 函數。
同樣的壓力測試函數也是有命名規則的,以Benchmark打頭,接上被測試函數的名字, 并且參數wei tesing.B 。同樣的,若果你在IDE中,寫完 Bench 也應該會有代碼補全提示。
func BenchmarkAdd(b *testing.B) {
b.ResetTimer()
for i := 0; i<b.N;i++ {
if result:=Add(1,9999999); result !=10000000 {
b.Errorf("Expected get %d, but got: %d ", 10000000 , result)
}
}
}
壓力測試同樣也可以在IDE中函數旁邊的小三角運作,但更多的使用的是指令行。可以使用下面兩條指令的任意一種。差別隻在于第一條指令指定了運作哪一個函數,第二條指令運作所有壓測函數。
go test -bench BenchmarkAdd -cpuprofile cpu.out
go test -bench . -cpuprofile cpu.out
go test -bench 函數名 指明了測試哪一個函數。 後面的 -cpuprofile cpu.out 則說明了監控CPU運作時的狀态并且輸出到了 一個 cpu.out 檔案。
安裝 graphviz
想要看這個 cpu.out 檔案需要用到一個 graphviz的插件,大家可以去官網直接下載下傳對應的版本即可。 下載下傳完成後将安裝目錄下的 bin 目錄加到環境變量中, 重新開機即可。如果不重新開機,在IDE中可能仍然無法使用,這是個大坑。 重新開機完成後在指令行輸入下面這條指令如果有輸出graphviz資訊,說明安裝成功
dot -version
回到剛剛的壓力測試,在輸出的 cpu.out 路徑下輸入下面這行指令:
go tool pprof cpu.out
然後會進入到 pprof 指令行, 我們可以輸入 web , 那麼則會彈出浏覽器得到一張圖,詳細展示了 運作過程中每一個函數的消耗時間。
你可能會好奇這張圖怎麼看。細節我也不太會,但是你隻要看到框框越大,顔色越紅,就代表這個函數消耗時間越多。
你也可以在 pprof 的指令行中輸入 top 獲得排名靠前的資源消耗函數。結果大概張這樣
Showing nodes accounting for 170ms, 100% of 170ms total
flat flat% sum% cum cum%
160ms 94.12% 94.12% 160ms 94.12% GoBasic/test.BenchmarkAdd
10ms 5.88% 100% 10ms 5.88% runtime.stdcall3
0 0% 100% 10ms 5.88% runtime.(*pageAlloc).scavenge
0 0% 100% 10ms 5.88% runtime.(*pageAlloc).scavengeOne
0 0% 100% 10ms 5.88% runtime.(*pageAlloc).scavengeRangeLocked
0 0% 100% 10ms 5.88% runtime.bgscavenge.func2
0 0% 100% 10ms 5.88% runtime.sysUnused
0 0% 100% 10ms 5.88% runtime.systemstack
0 0% 100% 160ms 94.12% testing.(*B).launch
0 0% 100% 160ms 94.12% testing.(*B).runN
喜歡軟體測試的小夥伴們,如果我的部落格對你有幫助、如果你喜歡我的部落格内容,請 “點贊” “評論” “收藏” 一 鍵三連哦!