測試
測試是自動化測試的簡稱,即編寫簡單的程序來確保程序(產品代碼)在測試中針對特定輸入產生預期的輸出。Go的測試方法看上去相對比較低級,它依賴於命令go test和一些能用go test運行的測試函數的編寫約定
go test 工具
go test 子命令是Go語言包的測試驅動程序,這些包根據某些約定組織在一起。在一個包目錄中,以_test.go結尾的文件不是go build命令編譯的目標,而是go test編譯的目標。
在*_test.go文件中,三種函數需要特殊對待,即
- 功能測試函數
- 基准測試函數 :名稱以benchmark開頭,用來檢測某些操作的性能。
- 示例運行測試函數 :以Example來測試某些操作的性能。用來提供機器檢查過的文檔。
Test 函數
每一個測試文件必須導入testing包,簽名如下
import "testing" func TestName(t *testing.T) { }
功能測試函數必須以Test開頭,可選的后綴名稱必須以大寫字母開頭:
import "testing" func TestSin(t *testing.T) { } func TestCos(t *testing.T) { }
示例
如下編譯一個測試連接mysql的單元測試,必須保證同目錄的情況下,否則找不到,創建一個mysql.go用來編寫連接的代碼
package mysql import ( "database/sql" _ "github.com/go-sql-driver/mysql" ) func findByPk(pk int) int { var num int = 0 db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/plugin?charset=utf8") if err != nil { panic(err.Error()) } defer db.Close() stmtOut, err := db.Prepare("select id from t_admin where id=?") if err != nil { panic(err.Error()) } defer stmtOut.Close() err = stmtOut.QueryRow(pk).Scan(&num) if err != nil { panic(err.Error()) } return num }
在同目錄下創建一個mysql_test.go用來編寫測試代碼
package mysql import ( "testing" ) func Test_findByPk(t *testing.T) { num := findByPk(1) t.Log(num) }
運行
- go test: 在當前目錄下運行所有測試用例
- go test -file *_test.go : 運行指定測試文件。如 go test -file mysql_test.go
- go test -V: 顯示詳細結果
- go test -run="Test_XXX" : 指定運行某個方法
測試覆蓋率
一個測試套件覆蓋待測試包的比例稱為測試的覆蓋率。覆蓋率無法直接通過數量來衡量。任何事情都是動態的。即使最微小的程序都無法精准的測量。語句覆蓋率是指部分語句在一次執行中至少執行一次。go語言中提供cover工具
# go test -cover PASS coverage: 100.0% of statements ok jdGoBackend/library/sdk/jd 0.017s
go還提供了將輸出打印報告的方法。
# go test -cover -coverprofile=c.out
然后可以通過html方法顯示,使用cover
工具來處理生成的記錄信息,該命令會打開本地的瀏覽器窗口生成一個HTML報告。
# go tool cover -html=c.out
基准測試
基准測試是在一定的工作負載之下檢測程序性能的一種方法,在Go里面,基准測試函數看上去像一個測試函數。但前綴是Benchmark並且擁有一個*testing.B參數用來提供大多數和*testing.T相同的方法。額外增加一些與性能檢測相關的方法。還提供了一個整數檢測成員N,用來檢測執行的次數。
基本格式
func BenchmarkName(b *testing.B){ // ... }
下面的基准測試在一個循環中調用了IsPaling共 N次
import "testing" func BenchmarkIsPaling(b *testing.B) { for i:=0; i< b.N; i++{ IsPaling("aaaaa") } }
和測試不同的是,默認情況下不會執行任何基准測試,需要標記-bench的參數指定要運行的基准參數。
# go test -bench=IsPaling BenchmarkIsPaling-8 10000000 203 ns/op PASS ok jdGoBackend/library/sdk/jd 2.255s
就上面的運行結果,基礎名稱BenchmarkIsPaling-8后綴8表示GOMAXPROCS的值。這個數字對並發基准測試很重要。
報告告訴我們每次IsPaling調用耗費0.23ms。這個是1 000 000 0次調用的平均值
我們可以通過-benchmen再報告中包含了內存分配統計數據。這里和優化之前的內存分配進行比較。
# go test -bench=. -benchmen // . 通配符匹配所有 PASS BenchmarkIsPaling 1000000 1026 ns/op 304 B/op 4 allocs/op
最后,性能比較函數知識普通的代碼,它們的表現形式通常是帶有一個參數的函數,被多個不同Benchmark函數傳入不同的值來調用。如下
func benchmark(b *testing.B, size int){/* ... */} func Benchmark10(b *testing.B){ benchmark(b, 10) } func Benchmark100(b *testing.B){ benchmark(b, 100) } func Benchmark1000(b *testing.B){ benchmark(b, 1000) }
參數size指定了輸入的大小,每個Benchmark函數傳入的值都不同但是在每個函數內部是一個常量,不要使用b.N作為輸入的大小。除非把它當作固定大小輸入的循環次數。否則該基准測試的結果毫無意義。
最后測試牽扯到性能,將會在性能模塊詳細介紹。