GO/testing包
前言
之前在寫GO
單元測試的時候, 使用了這個結構testing.T
. 進來無事翻了翻, 發現testing
包中還有一些其他的結構體, 想來是不同用處. 沒想到GO
的testing
包竟然默默做了這麼多支援, 之前竟然不知道.
在testing
包中包含一下結構體:
-
testing.T
: 這就是我們平常使用的單元測試 -
testing.F
: 模糊測試, 可以自動生成測試用例 -
testing.B
: 基準測試. 對函式的執行時間進行統計. -
testing.M
: 測試的鉤子函式, 可預置測試前後的操作. -
testing.PB
: 測試時並行執行.
依次對GO
的各個測試型別進行介紹.
以下各項測試中出現的方法Reverse
// 此方法源自 Go 官方文件
func Reverse(s string) string {
bs := []byte(s)
length := len(bs)
for i := 0; i < length/2; i++ {
bs[i], bs[length-i-1] = bs[length-i-1], bs[i]
}
return string(bs)
}
testing.T
用於進行單元測試. 官方文件
Go
對單元測試函式要求如下:
- 檔名形如:
xxx_test.go
- 函式簽名形如:
func TestXxx(t *testing.T)
我們建立檔案lib_test.go
func TestReverse(t *testing.T) { str := "abc" revStr1 := Reverse(str) revStr2 := Reverse(revStr1) if str != revStr2 { // error 方法報錯後, 會繼續向下執行 t.Error("error") // fatal 方法報錯後, 會退出測試 // t.Fatal("fatal") // 輸出除錯資訊 // t.Log("log") // 測試中斷, 但是測試結果不會十遍 // t.Skip("skip") } // 可啟動多個子測試, 子測試之間並行執行 for _, str = range []string{"abcd", "aceb"} { // 第一個引數為子測試的標識 t.Run(str, func(t *testing.T) { revStr1 := Reverse(str) revStr2 := Reverse(revStr1) if str != revStr2 { t.Error("error") } }) } }
使用如下命令執行測試用例(test.run 指定執行某一個函式):
go test -test.run TestReverse
這就是單元測試的簡單應用了, 是不是so easy
啦.
testing.F
用於模糊測試, 會自動生成測試用例. 官方文件
其內部會自動生成各種測試用例, 並自動呼叫執行. Go
對模糊測試的函式要求如下:
- 檔名形如:
xxx_test.go
- 函式簽名形如:
func FuzzXxx(f *testing.F)
其測試函式定義如下:
func FuzzReverse(f *testing.F) {
// 設定測試用例需要隨機生成的變數型別
f.Add("Hello, world!")
// 生成測試用例並進行測試. 回電函式接收的引數, 與 f.Add 設定的引數型別一致
f.Fuzz(func(t *testing.T, str string) {
revStr1 := Reverse(str)
revStr2 := Reverse(revStr1)
if revStr2 != str {
t.Error("error")
}
// 判斷是否是合法的 utf8 編碼
if utf8.ValidString(str) && !utf8.ValidString(revStr1) {
t.Error("utf8 error")
}
})
}
執行命令開始測試: go test -test.fuzz FuzzReverse -test.run ^$
(其中test.run
指定不執行test
函式)
當測試失敗的時候, 失敗的用力會寫入指定的檔案, 檔案在控制檯輸出.
testing.B
用於基準測試. 對函式的執行時間進行統計. , 對函式要求如下:
- 檔名形如:
xxx_test.go
- 函式簽名形如:
func BenchmarkXxx(b *testing.B)
函式定義如下:
func BenchmarkReverse(b *testing.B) {
// 開啟記憶體統計
b.ReportAllocs()
// 按照要求執行 n 遍
for i := 0; i < b.N; i++ {
Reverse("hello")
}
}
執行命令: go test -test.bench BenchmarkReverse -test.run ^$
結果中指出了執行次數及平均時間. 其中各項值得含義如下:
-
100000000
: 迭代次數 -
ns/op
: 平均每次迭代消耗的時間 -
B/op
: 平均每次迭代消耗的記憶體 -
allocs/op
: 平均每次迭代記憶體的分配次數
testing.M
定義在執行測試的前後執行的操作. 對函式的要求如下:
- 檔名形如:
xxx_test.go
- 函式簽名為:
func TestMain(m *testing.M)
函式定義如下:
func TestMain(m *testing.M) {
// 測試之前執行的操作
fmt.Println("starting test main")
// 執行測試
code := m.Run()
// 測試之後執行的操作
fmt.Println("ending test main")
os.Exit(code)
}
此函式會在執行所有測試時自動呼叫.
testing.PB
用於在測試時進行併發測試. 上面的 單元測試/模糊測試/基準測試 都可以使用. 以基準測試為例, 使用如下:
// 充分利用 CPU 資源, 並行執行 n 次
func BenchmarkReverse2(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
// 此迴圈體總共執行 b.N 次
Reverse("hello")
}
})
}
如此便可並行執行啦.
好, 有關Go
的單元測試, 到這裡就差不多了. 以上這些已經基本能夠滿足日常使用了