01
介紹
我們使用 Golang 語言開發的項目,怎么保證邏輯正確和性能要求呢?也就是說我們如何測試我們的 Golang 代碼呢?在 Golang 語言中,可以使用標准庫 testing 包編寫單元測試和基准測試,使用 go test
命令執行單元測試和基准測試的代碼。本文我們介紹在 Golang 語言中怎么編寫測試代碼。
02
命名規范
在 Golang 語言中編寫測試代碼,需要遵循一些命名規范,包含文件名、包名、函數(方法)名和變量名。
文件名和包名
測試文件名以 _test.go
結尾,go test
工具可以遍歷以 _test.go
結尾的文件,執行測試函數。而 go build
和 go run
會忽略以 _test.go
結尾的文件,文件名開頭一般是被測試函數所在的文件名。
包名一般和被測試文件的包名相同,這樣即可以測試被測試文件的可導出函數和不可導出函數。
函數名和方法名
測試函數(方法)名必須以 Test、Benchmark 和 Example 開頭,並且必須是可導出函數。函數名一般是被測試函數名,首字母大寫。如果我們需要給同一個函數編寫多個測試函數,可以在函數名后接上測試函數的場景,例如:TestXxxxXxxx
。
變量名
測試函數(方法)的變量名,Golang 語言和 go test
工具沒有明確的約束,但是,社區針對輸出結果有一些規范供大家參考。在編寫單元測試代碼時,一般會得到一個實際輸出結果,和一個我們預期的輸出結果做對比。針對這兩個變量,社區的變量名規范是 got/want
或 expected/actual
。
03
編寫測試代碼
單元測試
所謂單元測試,顧名思義就是對單元進行測試,一般進行測試的單元是一個最小的單元,在 Golang 語言中,最小的單元就是指一個函數或方法。
單元測試的函數,函數名以 Test
開頭,例如:TestXxx
。參數必須是 *testing.T
類型,可以使用該類型的方法記錄測試信息和測試狀態。例如,一般使用 Log
和 Logf
記錄測試信息,使用 Error
、Errorf
、Fatal
和 Fatalf
方法記錄測試狀態,該類型的更多方法可以閱讀官方文檔。
被測試函數:
func Sum(a, b int) int { return a+b }
測試函數:
func TestSum(t *testing.T) { a, b := 1,2 rst := Sum(a, b) if rst == 3 { t.Logf("expected=%d, actual=%d", 3, rst) } else { // t.Errorf("expected=%d, actual=%d", 3, rst) t.Fatalf("expected=%d, actual=%d", 3, rst) } t.Log("done") }
閱讀上面這段代碼,是我們編寫的 Sum 函數的單元測試,給定 a, b 兩個變量作為 Sum 函數的輸入參數,此外,我們還可以使用表格測試法,給定一組被測試函數的輸入參數,限於篇幅,本文不准備花費篇幅介紹。
使用 go test
命令執行以上單元測試的代碼:
go test PASS ok learn_go/lesson27 0.555s
go test
命令遍歷所有 _test.go
結尾的文件,執行文件中所有的測試函數。此外,go test
支持一些參數,例如,-v
輸出測試函數的運行詳情;-run
指定執行的測試函數;-count
指定執行次數。
此外,使用參數 --coverprofile
統計單元測試的覆蓋率。
go test --coverprofile=func.cover PASS coverage: 100.0% of statements ok learn_go/lesson27 0.499s
閱讀上面的執行結果,可以發現我們編寫的單元測試覆蓋率為 100%。
如果我們想要查看詳細的覆蓋率統計結果,我們可以執行以下命令生成 html
文件,使用瀏覽器打開生成的 html
文件,可以查看詳細的單元測試覆蓋率統計結果。
go tool cover -html=func.cover -o func_cover.html
運行以上命令,會生成一個名為 func_cover.html
的文件,我們可以使用瀏覽器打開它,查看詳細的單元測試覆蓋率統計結果。
基准測試
在 Golang 語言中,可以使用基准測試查看代碼的性能。基准測試的函數名以 Benchmark
開頭,例如:BenchmarkXxx
。參數必須是 *testing.B
類型,函數體中 for 循環的條件,以 b.N
作為循環次數,它是基准測試框架提供的,它在 Golang 運行時動態調整,通過多次測試,得到性能評估結果。
示例代碼:
func BenchmarkSum(b *testing.B) { for i := 0; i < b.N; i++ { Sum(1, 2) } }
我們可以使用 go test
工具執行以上基准測試的代碼,基准測試函數不會自動執行,必須使用參數 -bench
。
go test -bench=".*" goos: darwin goarch: amd64 pkg: learn_go/lesson27 cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz BenchmarkSum-16 1000000000 0.2325 ns/op PASS ok learn_go/lesson27 0.748s
閱讀上面的執行結果,我們主要介紹一下 BenchmarkXxx-n
這一行的意思。這一行共有三列,第一列 BenchmarkSum-16
分別代表基准測試的函數名和參與基准測試的 CPU 線程數,默認是 GOMAXPROCS 的值。第二列 1000000000
表示基准測試循環執行的次數。第三列 0.2325 ns/op
表示每次循環的平均執行耗時是 0.2325
納秒,該值越小,說明代碼性能越高。
除了 b.N
之外,還有幾個關於性能測試時間計數的方法,例如:b.ResetTimer()
、b.StopTimer()
和 b.StartTimer()
,我們可以根據我們的測試場景,靈活使用。
此外,go test
工具關於基准測試的參數,除了參數 -bench
之外,還有 -benchmem
統計內存分配;-cpu
指定參與執行基准測試的 CPU 線程數;-benchtime
指定測試時間和循環次數,其中值的單位為 s
表示指定執行多少秒,單位為 x
表示指定循環執行次數;-timeout
指定基准測試函數執行的超時時間。
04
總結
本文我們介紹怎么編寫測試代碼,包含單元測試和基准測試。特別需要注意的是一些命名規范。
養成編寫測試代碼的習慣,不僅可以降低代碼邏輯的錯誤率,而且在多人開發中,還可以提升聯調效率和提測通過率。
參考: