golang 單元測試 (https://github.com/stayfoo/stayfoo-hub)

stayfoo發表於2019-08-18

> 我的 golang 開發筆記:https://github.com/stayfoo/stayfoo-hub

單元測試

一般有三類測試:功能測試(test)、效能測試(benchmark,也叫基準測試)、示例測試(example)。

編寫規則

測試原始碼檔名稱要求:被測試原始碼檔案的名稱_test.go, eg:要對 demo.go 進行測試,測試檔案就是 demo_test.go

測試函式的名稱和簽名規定:

  • test 測試函式,名稱必須以 Test 為字首; 只能有一個引數, 且引數型別:*testing.T
  • benchmark 測試函式,名稱必須以 Benchmark 為字首; 只能有一個引數,且型別為:*testing.B
  • example 測試函式, 名稱必須以 Example 為字首;對引數列表沒有強制規定。(與功能測試相比,更關注程式列印出來的內容)

go test

go test 使測試程式碼執行,需要測試檔案編寫遵循上面的規則。

go test 執行的主要流程:

=== RUN   TestFail
--- PASS: TestFail (0.00s)
    main_test.go:49: Failed.
PASS
ok      code/test       0.006s

# ok: 說明測試結果和預期一樣
# code/test: 測試的程式碼包路徑
# 0.006s : 此次對該程式碼包的測試所耗費的時間
  • 引數 -run
#執行被測試包中的所有功能測試函式
go test
#同 go test
go test -run ''

#可以是一個函式的名字
go  test -run=TestHello

#匹配部分函式名, 匹配以 TestHel 開頭的測試函式, eg: TestHello
go  test -run=Hel
#同上
go test -run Hel

不執行任何功能測試函式 -run ^$

go test -run ^$

# 和其他引數一起使用,比如只想執行 benchmark 函式:
go test -run ^$ -bench .

測試程式碼:

func TestHello2(t *testing.T) {
    t.Run("A=1", func(t *testing.T) {
        fmt.Println("hello2 is A=1")
    })
    t.Run("A=2", func(t *testing.T) {
        fmt.Println("hello2 is A=2")
    })
    t.Run("B=1", func(t *testing.T) {
        fmt.Println("hello2 is B=1")
    })
}
# 匹配執行 所有 TestHello 開頭的測試函式,以及測試函式內部 A= 的子測試用例
go test -run Hello/A=

# 匹配執行 所有的測試函式,以及測試函式內部 A=1 的子測試用例
go test -run /A=1
  • 快取:
# 檢視快取目錄的路徑:
go env GO CACHE 

# 手動刪除所有快取
go clean -cache

# 刪除所有的測試結果快取;不會刪除任何構建結果快取。
go clean -testcache
#設定環境變數 GODEBUG
# go 命令繞過任何的快取資料,真正執行操作並重新生成所有結果,然後再去檢查新的結果與現有的快取資料是否一致
gocacheverify=1

Benchmark

go test 加上 -bench=. 才會執行效能測試。

# 值 . 表示執行任意名稱的效能測試函式
go test -bench=.

還可以是一個函式的名字 或者前面一部分:
go  test -bench=BenchmarkGetStore
go  test -bench=BenchmarkGetS
  • 只執行 Benchmark
# 和其他引數一起使用,比如只想執行 benchmark 函式:
go test -run ^$ -bench .
  • 執行結果解讀
func BenchmarkHandleData(b *testing.B)  {
    for i:=0; i<b.N; i++ {
        HandleData()
    }
}
go test -run ^$ -bench .

goos: darwin
goarch: amd64
BenchmarkHandleData-4        3000            401651 ns/op
PASS
ok      command-line-arguments  1.259s
#1. 執行的效能測試函式:BenchmarkHandleData  被測試的函式是:HandleData
#2. 執行時使用的最大 P 數量為 4
#3. 3000 是指被測函式 HandleData 的執行次數
#4. 401651 ns/op = 效能測試函式執行時間 / 被測函式執行次數; 單次執行被測函式的平均耗時為 401651 納秒;
BenchmarkHandleData-4        3000            401651 ns/op

> 最大 P 數量 ? > > P:processor ,是 G 和 M 的中介,使 G 與 M 對接並得到真正的執行。Go 執行時系統中,P 越多,承載 G 的佇列越多, G 可執行的越多(最大 P 代表同時執行 goroutine 的能力)。 每一條承載 G 的佇列,會不斷的把可執行的 G 給空閒的 M 去對接。對接完成,被對接的 G 就真正執行在作業系統的核心級執行緒上了。每條佇列之間會有聯絡,但都是獨立運作的。 > > G: goroutine ,協程 > > M: machine ,代表著系統級執行緒,即作業系統核心級別的執行緒 >

使用 go test -cpu 來設定最大 P 數量. 或者 呼叫 runtime.GOMAXPROCS 函式改變最大 P 數量。模擬被測試程式在不同計算能力的計算機中的表現。

一些函式

t.Log()

列印常規的測試日誌:(當測試成功的時候,不會列印)

t.Log()
t.Logf()

可以使用 -v 列印所有的常規測試日誌:

go test -v

t.Fail()

func TestFail(t *testing.T) {
    t.Fail()
    //t.FailNow() //會讓函式立即終止,該行程式碼之後的所有程式碼都不會執行。
    t.Log("Failed.")
}

對於失敗測試的結果, go test 命令並不會進行快取。 測試失敗,會把失敗函式裡面的常規日誌列印出來。(t.Log("Failed.")

t.Error()

t.Error() 相當於 t.Fail() + t.Log()

//相當於 t.Fail() + t.Log("Failed log.")
t.Error("Failed log.") 

t.Errorf() 相當於 t.Fail() + t.Logf()

//相當於 t.Fail() + t.Log("Failed log.")
t.Errorf("Failed log.") 

t.Fatal()

列印失敗錯誤日誌之後立即終止當前測試函式的執行,測試失敗。

相當於最後呼叫了 t.FailNow()

t.Fatal("Failed log.")
t.Fatalf("Failed log.")

> 我的 golang 開發筆記:https://github.com/stayfoo/stayfoo-hub

更多原創文章乾貨分享,請關注公眾號
  • golang 單元測試 (https://github.com/stayfoo/stayfoo-hub)
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章