Go的自動化測試
自動化測試,是在預設條件下執行系統或應用程式,評估執行結果,預先條件包括正常條件和異常條件。Go本身提供了一套輕量級的測試框架,相對低階,但不過還是有效的。它依賴一個go test
測試命令,和一組按照約定方式編寫的測試函式,符合規則的測試程式碼會在執行測試時被自動識別並執行。但是這些檔案是不會被普通的Go編譯器編譯,所以當將應用部署到生產環境時,它們不會被部署。
測試規則
檔案命名與放置規則
- 測試原始檔名應是
_test
結尾的go檔案,例如:add_test.go
; - 測試程式碼和業務程式碼是分開的,但兩者應該位於同一包下;
- 測試原始檔所在的包應該位於
$GOPATH/src
目錄下; - 測試檔案必須匯入
"testing"
在Go中,凡是以
_
或者.
符號作為檔名的首字母時,該檔案都會被構建工具忽略掉。以_test.go
結尾的測試檔案,將會被編譯為單獨的包,然後將其連結到主測試二進位制檔案。 go工具還會忽略名為testdata
的目錄,使其可以儲存測試所需的輔助資料。
測試函式命名規則
測試檔案包括三種函式:測試函式、基準測試函式和例子函式。測試函式用於測試程式的一些邏輯行為是否正確的函式,在命名時應該以Test
為字首,例如func TestAbcde(t *testing.T)
。基準測試用於衡量一些函式的效能,是以Benchmark
為函式名字首的函式,例如func BenchmarkAbcde(b *testing.B)
Example
為函式名字首的函式,例如:
func ExamplePrintln(){
Println("The output of\nthis example.")
//Output: The output of
//this example.
}
go test命令
go test
命令會自動測試匯入路徑命名的包並快取測試結果,根據測試的結果和預先設定的是否匹配,會返回PASS
或FAIL
。它的用法:go test [build/test引數] [包名]
,它有兩種不同的模式:本地目錄模式和包列表模式。僅在包列表模式下,go test
go test
將會重新顯示以前的輸出而不是再次執行測試二進位制檔案
本地目錄模式
這種模式是在沒有包引數的情況下,呼叫go test
時發生,例如:go test
或go test -v
。在此模式下,go test
會編譯當前目錄中的包和測試檔案,然後執行生成的二進位制檔案。這種模式下,快取是被禁用的。測試完成後,go test
會列印結果摘要,逐行顯示測試狀態,包名稱和已用的時間。
包列表模式
在呼叫go test
時傳入顯式包引數,例如:go test math
,go test ./...
,或者go test .
等。在此模式下,go test
會編譯和測試命令列中列出的每一個包。如果一個包測試通過,go test
只打印成功摘要,但如果測試失敗,則會列印完整的資訊。如果你傳入引數-bench
或-v
標誌,那麼go test
將會列印完整的輸出,即使是通過測試的包。第一個引數是當有基準測試函式時使用,第二種就是平常的測試函式就行。
顯式禁用快取的慣用方法是使用
-count=1
go test引數
-bench [正則表示式]:僅執行與正則表示式匹配的基準,預設情況下,不執行基準測試。如果要執行所有的基準測試函式,使用-bench .
或者-bench=.
。
-benchtime [時間]:表示執行多長時間的基準測試,預設值為1
秒。例如:-benchtime 1h30s
。
-count n:執行每個測試和基準多少次,預設為1
,如果設定了-cpu
,則為每個GOMAXPROCS
執行n
次,例子函式總是執行一次。
-cover:啟用覆蓋率分析,由於覆蓋率通過在編譯之前註釋原始碼來工作,因此啟用覆蓋率的編譯和測試失敗,可能會出現報告與原始檔案行號不對應的情況。
-covermode set,count,atomic:為正在測試的包設定覆蓋率分析模式,除非啟用-race
,否則預設為set
,在這種情況下它是原子的。
set
,布林值,表示這句宣告是否有效count
,int型別,這句宣告執行多少次atomic
,int型別,在多執行緒測試中使用,原子操作。
-coverpkg pattern1,pattern2,pattern3:將每個測試中的覆蓋率分析應用於模式匹配的包,預設情況下,每個測試僅分析正在測試的包。
-cpu 1,2,4:指定應為其執行測試或基準測試的GOMAXPROCS
的值的列表,預設值是GOMAXPROCS
的當前值。
-failfast:表示在第一次測試失敗後不要開始新的測試。
-list [正則表示式]:只是列出與正則表示式匹配的測試、基準測試或例子,不會執行任何測試。
-parallel n:表示執行的最大測試數,預設情況下,它設定為GOMAXPROCS
的值。
-run [正則表示式]:僅僅執行與正則表示式匹配的那些測試和例子,匹配時可能父項也會執行,例如:-run = X/Y
,匹配執行所有與X匹配的測試的結果,即使沒有匹配到子測試。
-timeout [時間]:如果測試檔案執行的時間超過設定的時間,就會出現panic
。如果設定的時間為0
,則表示timeout
不可用。預設是10
分鐘。
-v:會列印詳細的測試結果,即使是在測試成功的情況。
testing包
提供有關Go自動化測試的支援,它與go test
命令一起使用。
// TB 是型別T和B的介面.
type TB interface {
Error(args ...interface{}) //Fail+Log
Errorf(format string, args ...interface{})
Fail() //標記失敗,但繼續執行該測試函式
FailNow() //失敗,立即停止當前測試函式
Failed() bool
Fatal(args ...interface{}) //FailNow+Log
Fatalf(format string, args ...interface{})
Log(args ...interface{}) //輸出資訊,僅在失敗或-v引數時輸出
Logf(format string, args ...interface{})
Name() string
Skip(args ...interface{})
SkipNow() //跳過當前測試函式
Skipf(format string, args ...interface{})
Skipped() bool
Helper()
private()
}
例子
1.在你的$GOPATH/src
目錄下,建立一個目錄,例如:mytest
資料夾。
2.建立一個main
檔案,寫入業務程式碼。例如下面將0加到n的函式進行測試:
package main
func addNum(n int) (result int) {
for i := 0; i <= n; i++ {
result = result + i
}
return
}
3.建立測試檔案,檔名以_test.go
結尾,例如:main_test.go
。
package main
import (
"testing"
)
func TestAddNum(t *testing.T) {
if addNum(100) != 5050 {
t.Fatal("addNum error!")
}
}
4.在命令列裡,直接輸入go test
,運行當前包的所有測試檔案。或者執行go test mytest
,只測試mytest
包的測試檔案。
本地目錄方式執行時的返回結果:
PASS
ok mytest 0.421s
包列表模式執行的返回結果:ok mytest 0.138s
。
5.在main.go
檔案中,新增基準測試函式。
package main
import (
"testing"
)
func TestAddNum(t *testing.T) {
if addNum(100) != 5050 {
t.Fatal("addNum error!")
}
}
func BenchmarkAddNum(b *testing.B) {
for i := 0; i < b.N; i++ {
if addNum(100) != 5050 {
b.Fatal("addNum")
}
}
}
6.執行基準測試,go test -bench .
,預設情況下,go test
不會執行效能測試函式。效能測試需要執行足夠多的次數才能計算單次執行平均時間。
goos: windows
goarch: amd64
pkg: mytest
BenchmarkAddNum-4 10000000 187 ns/op
PASS
ok mytest 2.522s
報告顯示執行addNum函式花費的平均時間是2.522s,執行了10000000次,每282ns的速度執行一次迴圈。因為基準測試驅動器並不知道每個基準測試函式執行所花的時間,因此它會在真正執行基準測試前先試用較小的N來執行測試,估算基準測試函式所需要的時間,然後推斷一個較大的時間保證穩定的測量結果。 迴圈在基準測試函式內實現,而不是放在基準測試框架內實現,這樣可以讓每個基準測試函式有機會在迴圈啟動前執行初始化程式碼,這樣並不會顯著影響每次迭代的平均執行時間。 如果在執行基準測試之前需要一些費事的設定,則可能會重置計時器。例如:
func BenchmarkAddNum(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
if addNum(100) != 5050 {
b.Fatal("addNum")
}
}
}
參考文章: