Go 常用包: 單元測試 -testing-

  1. 介紹

testing 包爲Go 語言提供自動化測試的支持。通過 go test 命令來執行單元測試文件,單元測試文件命名格式爲: xxx_test.go, 在單元測試文件中, 根據測試類型不同可以分爲: 功能測試函數、基準測試函數, 區別如下:

Ks7CQf

  1. 功能測試

2.1 編寫規範

1. 函數格式

// 導入測試包
import "testing"
// 功能測試函數名
func TestName(t *testing.T) {
 t.Log("附加的日誌信息")
 // 功能邏輯代碼
 if true {
  // 錯誤時調用t.Error
  t.Error("報告測試失敗")
 }
}

整理規則如下:

2. 運行格式

通過在go test命令後添加-run參數,其值對應的是個正則表達式,只有匹配上的函數纔會被go test命令執行,如下示例:

# 執行命令
go test -run=? 文件[目錄]

-run值說明

iTQ91I

2.2 測試示例

1. 代碼詳情

文件名:go_test.go

package test

import (
 "testing"
)

// 通過測試函數
func TestPass(t *testing.T) {
 t.Log("這個是通過測試函數")
}

// 不通過測試函數
func TestFail(t *testing.T) {
 t.Error("運行測試失敗!")
}

2. 運行全部函數

➜  go test go_test.go -v
=== RUN   TestPass
    go_test.go:9: 這個是通過測試函數
--- PASS: TestPass (0.00s)
=== RUN   TestFail
    go_test.go:14: 運行測試失敗!
--- FAIL: TestFail (0.00s)
FAIL
FAIL    command-line-arguments  1.635s
FAIL

go test命令添加-v參數,可以查看測試函數名稱和運行時間

3. 運行指定函數

go test命令後添加-run參數,其值對應的是個正則表達式,只有匹配上的函數纔會被go test命令執行,如下示例:

# 只執行函數TestPass
➜ go test go_test.go -v -run="Pass"
=== RUN   TestPass
    more_func_test.go:9: 這個是通過測試函數
--- PASS: TestPass (0.00s)
PASS
ok      command-line-arguments  0.889s
# 只執行TestFail函數
➜  go test go_test.go -v -run="Fail"
=== RUN   TestFail
    more_func_test.go:14: 運行測試失敗!
--- FAIL: TestFail (0.00s)
FAIL
FAIL    command-line-arguments  0.198s
FAIL

2.3 子測試

Go1.7+後新增了子測試,我們可以通過使用t.Run來執行子測試, 具體使用如下:

1. 代碼

package test

import (
 "testing"
)

// 子測試使用
func TestSubtest(t *testing.T) {
 // 子測試A
 t.Run("subA", func(t *testing.T) {
  t.Error("subA測試失敗")
 })
 // 子測試B
 t.Run("subB", func(t *testing.T) {
  t.Log("subB測試成功!")
 })
}

2. 運行

➜ go test sub_test.go -v                    
=== RUN   TestSubtest
=== RUN   TestSubtest/subA
    sub_test.go:11: subA測試失敗
=== RUN   TestSubtest/subB
    sub_test.go:15: subB測試成功!
--- FAIL: TestSubtest (0.00s)
    --- FAIL: TestSubtest/subA (0.00s)
    --- PASS: TestSubtest/subB (0.00s)
FAIL
FAIL    command-line-arguments  0.851s
FAIL
  1. 基準測試

3.1 編寫規範

1. 函數格式

基準測試就是在一定的工作負載之下檢測程序性能的一種方法。基準測試函數的格式如下:

// 導入測試包
import "testing"
// 功能測試函數名
func BenchmarkName(b *testing.B){
   // 被測試代碼放到循環內 
    for i:=0;i<b.N;i++{
     // 具體業務函數
   }
}

整理規則如下:

2. 運行格式

# 執行命令
go test -bench=? 文件[目錄]

Go中我們還是通過go test來執行基準測試,區別是需要加上參數-bench=?, 而其中的?代表匹配函數名的正則表達式,匹配規則如下:

WtSmut

3.2 測試示例

1. 代碼詳情

package tmp

import (
 "testing"
)

// 變量賦值
func BenchmarkVar(b *testing.B) {
 strSlice := make([]string,10)
 for i := 0; i < b.N; i++ {
  strSlice = append(strSlice,"go")
 }
}
// 字符串拼接
func BenchmarkMulti(b *testing.B) {
 str := ""
 for i := 0; i < b.N; i++ {
  str = str + strconv.Itoa(i)
 }
}

@注意: 默認情況下,每個基準測試至少運行 1 秒。如果在 Benchmark 函數返回時沒有到 1 秒,則 b.N 的值會增加,並且函數會再次運行。

2. 運行全部函數

# 執行/test/bench_test.go文件中的所有Benchmark*函數
➜ go test -bench=. ./test/bench_test.go 
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkSum-12         1000000000               0.2574 ns/op
BenchmarkMulti-12       1000000000               0.2529 ns/op
PASS
ok      command-line-arguments  1.187s

3. 運行指定函數

# 只匹配BenchmarkSum*的函數
➜ go test -bench=Sum ./test/bench_test.go
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkSum-12         1000000000               0.2588 ns/op
PASS
ok      command-line-arguments  0.457s

4. 測試結果說明

goos: darwin # 執行系統,常用的值:linux, windows, drawin (macOS)
goarch: amd64 # CPU 架構,常用的值 amd64, arm64, i386, armhf
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz # CPU信息
#-12代表對應的 GOMAXPROCS 的值 
BenchmarkVar-12         23692479(執行次數)   48.77 ns/op #(每次耗時48.77ns)
BenchmarkMulti-12         348254          110975 ns/op
PASS
ok      command-line-arguments  43.349s

5. 更多維度數據

可以通過添加參數-benchmem來獲取更多的性能數據,執行如下:

➜ go test -bench=. ./test/bench_test.go  -benchmem
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkVar-12    18029002   59.87 ns/op    89 B/op      0 allocs/op
BenchmarkMulti-12  330717     102558 ns/op   900342 B/op  2 allocs/op
PASS
ok      command-line-arguments  36.462s

3Qust0

3.3 提高精準度

1. 提高運行時間

默認情況下,每個基準測試至少運行 1 秒。如果在Benchmark函數返回時沒有到 1 秒,則b.N的值會自增加,並且函數再次運行。如果想運行更長時間,可以通過參數-benchtime設置,如-benchtime=5s代表最少運行5秒,下面是兩種情況的使用方法

a. 被測代碼詳情

package test

import (
 "fmt"
 "testing"
)
// 測試函數Sprintf性能
func BenchmarkCompute(b *testing.B) {
 b.Logf("b.N=%d",b.N)
 for i := 0; i < b.N; i++ {
  _ = fmt.Sprintf("成績:%d",80)
 }
}

b. 默認運行時間

# 默認運行
➜ go test -bench=Compute ./test/bench_test.go              
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkCompute-12     13177644                81.26 ns/op
--- BENCH: BenchmarkCompute-12
    bench_test.go:10: b.N=1
    bench_test.go:10: b.N=100
    bench_test.go:10: b.N=10000
    bench_test.go:10: b.N=1000000
    bench_test.go:10: b.N=13177644
PASS
ok      command-line-arguments  1.556s

b. 設置至少運行時間

# 通過參數-benchtime=5s,設置至少運行5秒
➜ go test -bench=Compute ./test/bench_test.go -benchtime=5s
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkCompute-12     69329104                82.30 ns/op
--- BENCH: BenchmarkCompute-12
    bench_test.go:10: b.N=1
    bench_test.go:10: b.N=100
    bench_test.go:10: b.N=10000
    bench_test.go:10: b.N=1000000
    bench_test.go:10: b.N=69329104
PASS
ok      command-line-arguments  6.208s

@注意: 通過上面代碼發現不管是運行 1.5 秒還是運行 6.2 秒,每次消耗時間沒有太大差異,從側面說明被測函數性能穩定。

2. 設置次數運行結果

默認每次都是運行一次基準測試函數活的一次運行的結果, 但是可以通過參數-count來設置獲取運行多次的結果,具體使用如下:

執行上面示例:

# -count=5
➜ go test -bench=Compute ./test/bench_test.go -count=5
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkCompute-12     13069844                81.35 ns/op
BenchmarkCompute-12     14387593                81.51 ns/op
BenchmarkCompute-12     14359526                82.72 ns/op
BenchmarkCompute-12     14074830                83.99 ns/op
BenchmarkCompute-12     14114455                81.53 ns/op
PASS
ok      command-line-arguments  6.523s

3.4 計時方法

1. 函數列表

uWBNyA

進行基準測試之前可能會做一些準備,比如構建測試數據等,這些準備也需要消耗時間,所以需要把這部分時間排除在外。這時候我們可以使用 ResetTimer 方法來重置計時器,避免準備數據的耗時對測試數據造成干擾

2. 使用示例

// 重置時間使用
func BenchmarkTime(b *testing.B) {
 // 準備工作
 time.Sleep(time.Second * 3)
 // 重置時間
 b.ResetTimer()
 for i := 0; i < b.N; i++ {
  _ = fmt.Sprintf("hello:%v","word")
 }
}

3.5 並行測試

在基準測試中可以使用RunParallel函數, 來運行並行測試,它創建多個goroutine,並在其中分配b.N個迭代。goroutine的數量默認爲GOMAXPROCS。要想增加非CPU基準測試的並行度,可以在調用RunParallel之前調用SetParallelism。也可以通過-cpu=來設置使用。函數簽名具體如下:

func (b *B) RunParallel(body func(*PB))

body將在每個goroutine中運行。它應該設置任何goroutine-local 狀態,然後迭代直到pb.Next返回false。它不應使用StartTimerStopTimerResetTimer函數,因爲它們具有全局作用。它也不應調用運行。

1. 函數格式

func BenchmarkXXX(b *testing.B) {
 // b.SetParallelism(1) // 設置使用的CPU數
 b.RunParallel(func(pb *testing.PB) {
  for pb.Next() {
   // 調用具體函數
  }
 })
}

2. 代碼示例

// 並行測試
func BenchmarkParallel(b *testing.B) {
 b.RunParallel(func(pb *testing.PB) {
  for pb.Next() {
   _ = fmt.Sprintf("成績:%d",80)
  }
 })
}

4. 運行測試

# 不帶-cpu
➜ go test -bench=Parallel ./test/bench_test.go
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkParallel-12            50117173                22.84 ns/op
PASS
ok      command-line-arguments  1.788s
# 設置CPU=4
➜ go test -bench=Parallel ./test/bench_test.go -cpu=4
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
BenchmarkParallel-4     35953536                32.66 ns/op
PASS
ok      command-line-arguments  4.240s
  1. 報告函數

類型testing.T和testing.B都繼承了類型testing.common,testing.common常用的報告函數,整理如下:

Whtv5H

@注意: 上表中說的測試中斷,都是指中斷其所在的測試函數。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/MUvE3vomeoNsWovSBapLTg