如何用好Go的測試黑科技

測試是每個開發人員都須要掌握的技能,儘管你不須要像測試人員那麼專業,但你也應該儘量的作到那麼專業,據我瞭解到我身邊的一些Go開發人員,他們對Go的測試僅僅侷限於寫一個_test.go 測試文件,對執行方法進行測試,而後在goland的Ide中右鍵run方法運行,觀測結果是否爲綠色,僅此而已,我想說的是這只是一些皮毛,因此今天分享一些Go的測試技能,但願你們有收穫。html

Go測試用例

Go的測試文件命名規則爲xxx_test.go,其中xxx是須要測試的源代碼文件的名稱。在test文件中,能夠編寫測試函數,Go的測試函數整理分爲4種,以下,其中XXX是須要測試的方法名稱web

  1. 單元測試:TestXXX(t *testing.T)
  2. 基準測試:BenchmarkXXX(b *testing.B)
  3. Main函數測試:TestMain(m *testing.M)
  4. 控制檯測試 ExampleXXX()

假設咱們有一個array_utils.go的源代碼文件,包名爲array_utils,咱們在該包建立一個測試文件,名稱爲:array_utils_test.go文件,源代碼文件中有一個求最大子序列和的方法,咱們針對該方法測試,以下代碼,_test.go文件中能夠有任意多個測試方法,這些測試方法的合集被稱做測試套件。正則表達式

咱們的源碼文件以下:
package array_utils
 //求最大子序列和 (就是說子序列加起來和最大)
 func FindMaxSeqSum(array []int) int {
   SeqSum := make([]int, 0) // 存儲子序列和
   // 初始子序列和爲 數組下標爲0的值
   SeqSum = append(SeqSum, array[0])
   for i := 1; i < len(array); i++ {
      if array[i] > SeqSum[i-1]+array[i] {
         SeqSum = append(SeqSum, array[i])
      } else {
         SeqSum = append(SeqSum, SeqSum[i-1]+array[i])
      }
   }
   max := SeqSum[0]
   for j := 1; j < len(SeqSum); j++ {
      if SeqSum[j] > SeqSum[j-1] {
         max = SeqSum[j]
      }
   }
   fmt.Println(max) //打印結果
   return max
}
咱們的測試文件以下
package array_utils

import (
   "fmt"
   "os"
   "testing"
)
//TestMain會在下面全部測試方法執行開始前先執行,通常用於初始化資源和執行完後釋放資源
func TestMain(m *testing.M) {
   fmt.Println("初始化資源")
   result := m.Run() //運行go的測試,至關於調用main方法
   fmt.Println("釋放資源")
   os.Exit(result) //退出程序
}
//單元測試
func TestFindMaxSeqSum(t *testing.T) {
   sum := FindMaxSeqSum([]int{1, 3, -9, 6, 8, -19})
   if sum == 14 {
      t.Log("successful")
   } else {
      t.Error("failed")
   }
}
//基準測試
func BenchmarkFindMaxSeqSum(b *testing.B) {
   for i := 0; i < b.N; i++ {
      FindMaxSeqSum([]int{1, 3, -9, 6, 8, -19})
   }
}
//這個驗證的是FindMaxSeqSum方法控制檯輸出的max和OutPut後面的14是否一致,若是相同,則表示驗證經過,不然測試用例失敗
func ExampleFindMaxSeqSum() {
   FindMaxSeqSum([]int{1, 3, -9, 6, 8, -19})
   // OutPut: 14
}

咱們經過命令go test來運行這段測試代碼,進入到array_utils包下面,go test會遍歷當前包下全部的xxx_test.go中符合上述命名規則的函數,而後生成一個臨時的main包用於調用相應的測試函數,而後構建並運行、報告測試結果,最後清理測試中生成的臨時文件,結果以下:數組

初始化資源
=== RUN   TestFindMaxSeqSum
14
--- PASS: TestFindMaxSeqSum (0.00s)
    array_utils_test.go:16: successful
PASS
釋放資源

Go test 有兩種運行模式

本地目錄模式,在沒有包參數(例如 go test 或 go test -v )調用時發生。在此模式下, go test 編譯當前目錄中找到的包和測試,而後運行測試二進制文件。在這種模式下,caching 是禁用的。在包測試完成後,go test 打印一個概要行,顯示測試狀態、包名和運行時間瀏覽器

  1. go test 當軟件包屬於$GOPATH時,不須要從 Go Module 內部執行此命令,就進行測試

包列表模式,在使用顯示包參數調用 go test 時發生(例如 go test math , go test ./... 甚至是 go test .)。在此模式下,Go測試編譯並測試在命令上列出的每一個包。若是一個包測試經過, go test 只打印最終的 ok 總結行。若是一個包測試失敗, go test 將輸出完整的測試輸出。若是使用 -bench 或 -v 標誌,則 go test 會輸出完整的輸出,甚至是經過包測試,以顯示所請求的基準測試結果或詳細日誌記錄緩存

  1. go test math ,指的是測試Go的SDK的math包中的test文件
  2. go test ./... , 指的是測試遞歸測試當前目錄下的全部test文件,由於當前目錄下還有還有子文件夾,子文件夾下面還有子文件夾。
  3. go test . 測試當前目錄中的軟件包
  4. go test ./tranform 來測試 ./tranform 目錄中的包

在包列表模式下,Go緩存成功的測試結果,以免重複運行相同的測試。每當 GO 在包上運行測試時,Go都會建立一個測試二進制文件並運行它,若是要全局禁用緩存,能夠將GOCACHE環境變量設置爲off,安全

set GOCACHE=off  //關閉,go1.12版本後必須打開,不然編譯器報錯
set GOCACHE=on  //開啓
go clean -testcache  //手動清除緩存

Go的test經常使用參數實踐

上面咱們只是執行了go test的命令,關於go test可能的flag還有不少,不一樣的flag其對應的功能不一樣,接下來咱們來實踐一下。多線程

基礎功能參數

go test -c 生成用於運行測試的可執行文件,但不執行,在window平臺下生成的是.exe文件,截圖以下
併發

go test -i 安裝/從新安裝運行測試所需的依賴包,但不編譯和運行測試代碼。app

go test -o array_utils.test.exe 運行指定的的可執行的測試文件

go test -v 輸出打印有關測試函數的其它信息

go test -v array_utils_test.go array_utils.go 測試指定的的文件

go test -v array_utils_test.go array_utils.go -test.run TestFindMaxSeqSum 測試指定文件的指定方法

go test -v -run=TestFindMaxSeqSum 測試指定文件的指定方法,-run後面能夠匹配正則表達式,這個指的是測試名字等於TestFindMaxSeqSum的方法,若是是多個方法的話,可使用|來隔開方法名。

代碼覆蓋率

由單元測試的代碼,觸發運行到的被測試代碼的代碼行數佔全部代碼行數的比例,被稱爲測試覆蓋率,代碼覆蓋率不必定徹底精準,可是能夠做爲參考,能夠幫咱們測量和咱們預計的覆蓋率之間的差距

go test -cover 生成代碼測試覆蓋率 ,coverage: 8.1% of statements

go test -v -coverprofile=c.out 將生成的代碼測試覆蓋率放入一個文件,而後運行下面的命令能夠將c.out文件轉換成一個html文件用瀏覽器閱讀,截圖以下,no coverage 表明沒有覆蓋的代碼,high coverage表明高覆蓋率的表明,一個紅色,一個綠色,這裏紅色的截圖上沒體現出來,你們可本地試驗一下。

go tool cover -html=c.out -o=tag.html

go test -covermode=set 覆蓋測試模式,有三種值set,count,atomic,其中set表明的是這個語句運行嗎?count表明的是這個語句執行多少次,atomic表明的是多線程正確使用的,耗資源的。

基準測試

go test默認狀況下只會運行單元測試,那麼基準測試如何執行呢?接下來一塊兒看看。

go test -bench=. 執行當前測試包下的基準測試方法,在執行過程當中會根據實際case的執行時間是否穩定會增長b.N的次數,要注意若是是要測試一個非穩態的函數,那麼它可能永遠也執行不完,記住-bench後面跟的是正則表達式

這是執行結果,-8指的是運行時對應的 GOMAXPROCS 的值,5000000指的是for循環的次數,249 ns/op 指的是每一次循環耗時239納秒
BenchmarkFindMaxSeqSum-8         5000000               249 ns/op

go test -run=none -bench=. 經過指定方法名稱爲none來過濾掉單元測試,只執行基準測試的方法,固然也能夠根據-bench後面的正則表達式來匹配。

go test -benchtime=3s -bench=. 在持續時間3s內運行每一個基準測試

go test -benchmem -bench=. 打印基準測試時的內存分配

120 B/op表明每次操做消耗120B內存(1kb=1024b),  4 allocs/op 表明每次操做分配內存的次數
BenchmarkFindMaxSeqSum-8   5000000   300 ns/op    120 B/op    4 allocs/op

go test -count=2 -bench=. 執行指定次數的基準測試,在-count=1時至關於禁用緩存

go test -cpu=1 -bench=. 設置指定的cpu數量來進行基準測試,能夠指定多個不一樣的cpu個數列別,好比:-cpu=1,2,4

其它一些參數控制

go test -timeout=3s默認狀況下,測試執行超過10分鐘就會超時而退出,咱們能夠經過這個時間指定超時時間

go test -parallel=2 當測試使用t.Parallel()方法將測試轉爲併發時,將受到最大併發數的限制,默認狀況下最多有GOMAXPROCS個測試併發,其餘的測試只能阻塞等待,這個能夠用來併發安全的測試。

go test -short 縮短長時間運行的測試的測試時間。默認關閉

go test -v -cpuprofile=cpuprof.out 生成cpuprof的文件,經過運行下面的命令能夠查看cpuprof的文件,默認是在控制檯查看,固然也能夠web界面查看,這不是本篇文章的重點,後面會單說。

go tool pprof prof.out

go test -trace trace.out 在退出以前,將執行跟蹤寫入指定文件。

go test -race 檢測併發狀況下數據競爭的問題,這個的使用比較複雜,後面也會單寫文章來介紹。

testing 的變量

test.short : 一個快速測試的標記,在測試用例中可使用 testing.Short() 來繞開一些測試
test.outputdir : 輸出目錄
test.coverprofile : 測試覆蓋率參數,指定輸出文件
test.run : 指定正則來運行某個 / 某些測試用例
test.memprofile : 內存分析參數,指定輸出文件
test.memprofilerate : 內存分析參數,內存分析的抽樣率
test.cpuprofile : cpu 分析輸出參數,爲空則不作 cpu 分析
test.blockprofile : 阻塞事件的分析參數,指定輸出文件
test.blockprofilerate : 阻塞事件的分析參數,指定抽樣頻率
test.timeout : 超時時間
test.cpu : 指定 cpu 數量
test.parallel : 指定運行測試用例的並行數

還有不少,須要讀者們自行研究,總結,分享,能夠留言區討論

testing.T 和 testing.B

testing 包內的結構

B : 壓力測試
BenchmarkResult : 壓力測試結果
Cover : 代碼覆蓋率相關結構體
CoverBlock : 代碼覆蓋率相關結構體
InternalBenchmark : 內部使用的結構
InternalExample : 內部使用的結構
InternalTest : 內部使用的結構
M : main 測試使用的結構
PB : Parallel benchmarks 並行測試使用結果
T : 普通測試用例
TB : 測試用例的接口

testing包的方法

若是有幫助,關注下公衆號,閱讀更多精彩文章

相關文章
相關標籤/搜索