在包目錄內,所有以_test.go
為后綴名的源文件在執行go build時不會被構建成包的一部分,它們是go test測試的一部分。
在*_test.go
文件中,有三種類型的函數:測試函數、基准測試(benchmark)函數、示例函數。一個測試函數是以Test為函數名前綴的函數,用於測試程序的一些邏輯行為是否正確;go test命令會調用這些測試函數並報告測試結果是PASS或FAIL。基准測試函數是以Benchmark為函數名前綴的函數,它們用於衡量一些函數的性能;go test命令會多次運行基准函數以計算一個平均的執行時間。示例函數是以Example為函數名前綴的函數,提供一個由編譯器保證正確性的示例文檔。
本節主要簡單介紹一下測試函數,以及go test的一些常用命令。
現在的目錄結構如下:在word包下,含有兩個文件,一個是word.go,該文件內有一個判斷字符串是否是回文串的函數。另一個是測試文件word_test.go。
D:\workspace\GoRepo\gopl\ch11\word>ls
word.go word_test.go
word.go文件
package word import "unicode" func IsPalindrome(s string) bool { // 處理非ASCII字符,比如中文 var letters []rune for _, r := range s { if unicode.IsLetter(r) { letters = append(letters, unicode.ToLower(r)) } } length := len(letters) for i := 0; i < length/2; i++ { if letters[i] != letters[length-i-1] { return false } } return true }
一個簡單的測試函數可以寫成如下,每個測試函數必須導入testing包。測試函數有如下的簽名:
func TestName(t *testing.T) { // ... }
特別注意,測試函數的名字必須以Test開頭,可選的后綴名必須以大寫字母開頭,比如
func TestFoo(t *testing.T) { // 正確 // ... } func Testfoo(t *testing.T) { // 錯誤 // ... }
一個簡單的測試文件可以寫成這樣:
package word import "testing" func TestIsPalindrome(t *testing.T) { if !IsPalindrome("detartrated") { t.Error(`IsPalindrome("detartrated") = false`) } if !IsPalindrome("kayak") { t.Error(`IsPalindrome("kayak") = false`) } } func TestNonPalindrome(t *testing.T) { if IsPalindrome("abcd") { t.Error(`IsPalindrome("abcd") = true`) } }
go test 命令如果沒有參數指定具體哪個包,那么將默認采用當前目錄對應的包(和go build命令一樣)。我們可以用下面的命令構建和運行測試。
D:\workspace\GoRepo\gopl\ch11\word1>go test PASS ok _/D_/workspace/GoRepo/gopl/ch11/word1 0.955s
如果顯示PASS,說明所有測試用例均已經通過。
參數-v
可用於打印每個測試函數的名字和運行時間:
D:\workspace\GoRepo\gopl\ch11\word1>go test -v === RUN TestIsPalindrome --- PASS: TestIsPalindrome (0.00s) === RUN TestNonPalindrome --- PASS: TestNonPalindrome (0.00s) PASS ok _/D_/workspace/GoRepo/gopl/ch11/word1 1.963s
參數-run
對應一個正則表達式,只有測試函數名被它正確匹配的測試函數才會被go test
測試命令運行:
D:\workspace\GoRepo\gopl\ch11\word1>go test -v -run="Non" === RUN TestNonPalindrome --- PASS: TestNonPalindrome (0.00s) PASS ok _/D_/workspace/GoRepo/gopl/ch11/word1 0.870s
此外,還有-race參數也比較常用,用於檢測是否存在數據競爭非常有用,這個點在用到的時候再講。
最后,在寫測試文件的時候,推薦將所有測試數據合並到一個測試的表格中。比如,寫成下面這樣子。這種表格驅動的測試在Go語言中很常見。我們可以很容易地向表格添加新的測試數據,並且后面的測試邏輯也沒有冗余,這樣我們可以有更多的精力去完善錯誤信息。
func TestAllCases(t *testing.T) { // 把所有測試用例裝在一個結構體數組中 var tests = []struct { input string want bool } { {"",true}, {"a",true}, {"aa",true}, {"abba",true}, {"ab",false}, {"A man, a plan, a canal: Panama",true}, {"哈哈哈",true}, } for _, test := range tests { // 如果返回結果不和預期的一致,則測試不通過 if res := IsPalindrome(test.input); res != test.want { t.Errorf("IsPalindrome(%q) = %v", test.input, res) } } }