11 Go測試

一、測試基礎(chǔ)概述

Go語言內(nèi)建一套基于表格驅(qū)動測試的機(jī)制,只需編寫少量代碼,就可對特定功能進(jìn)行單元測試和基準(zhǔn)測試(性能測試或壓力測試)

二、單元測試

所謂單元測試,是程序中對最小功能單元進(jìn)行檢查和驗(yàn)證。

Go進(jìn)行單元測試非常簡單:

  • 創(chuàng)建一個go文件,在命名文件時以_test.go結(jié)尾,例如: unit_test.go

  • 每個測試文件可支持多個測試用例的編寫,每個測試用例函數(shù)需要以Test為前綴,且其只有一個*testing.T指針類型的入?yún)?。例如?/p>

func TestAXXX( t *testing.T ){}

func TestBXXX( t *testing.T ){}
  • 運(yùn)行g(shù)o test -v {testing_filename.go} 執(zhí)行測試并打印測試詳細(xì)數(shù)據(jù)
go test 指定文件時默認(rèn)執(zhí)行文件內(nèi)的所有測試用例??梢允褂?run參數(shù)選擇需要的測試用例單獨(dú)執(zhí)行
以下對一個斐波那契數(shù)列計算函數(shù)的測試示例

測試目標(biāo)功能函數(shù):

//遞歸實(shí)現(xiàn)的斐波那契數(shù)列
func Fibonacci(n int) int {
    rs := 0
    //歸
    if n == 1 || n == 2 {
        return 1
    }
    //遞
    rs = Fibonacci(n-1) + Fibonacci(n-2)
    return rs
}

單元測試用例:

import "testing"

func TestFibonacci(t *testing.T) {
    //先編一個預(yù)定義的參數(shù)和結(jié)果集
    caseMap := make(map[int]int)
    caseMap[1] = 1
    caseMap[3] = 2
    caseMap[5] = 5
    caseMap[10] = 55

    //測試運(yùn)行一個函數(shù),得到結(jié)果
    for i, v := range caseMap {
        rs := Fibonacci(i) //目標(biāo)測試函數(shù)
        if rs != v {
            t.Fatalf("測試用例發(fā)現(xiàn)錯誤:參數(shù)%d,預(yù)計得到結(jié)果%d,實(shí)際得到結(jié)果%d", i, v, rs)
        }
    }

    t.Log("測試結(jié)束,沒有發(fā)現(xiàn)問題。")

}

//OUTPUT SUCCESSFUL:
=== RUN   TestFibonacci
--- PASS: TestFibonacci (0.00s)
    TestUnit_test.go:48: 測試結(jié)束,沒有發(fā)現(xiàn)問題。
PASS
ok      command-line-arguments  0.005s

//OUTPUT FAIL:
//為模擬測試失敗,更改一個預(yù)定義結(jié)果集讓其不通過
=== RUN   TestFibonacci
--- FAIL: TestFibonacci (0.00s)
    TestUnit_test.go:44: 測試用例發(fā)現(xiàn)錯誤:參數(shù)10,預(yù)計得到結(jié)果50,實(shí)際得到結(jié)果55
FAIL
FAIL    command-line-arguments  0.005s


//注意:在使用go test 測試單個文件的時候,本人運(yùn)行出現(xiàn)報錯:
# command-line-arguments [command-line-arguments.test]
Code/go/src/mydemo/base/TestUnit_test.go:42:9: undefined: Fibonacci
FAIL    command-line-arguments [build failed]

//可看到對測試目標(biāo)運(yùn)行測試時沒有發(fā)現(xiàn)目標(biāo)函數(shù),看到后面[build failed]失敗了,這里需要把測試目標(biāo)的文件也添加到命令:
go test -v {testing_filename.go} {target_filename.go}

以上為最簡單的功能測試實(shí)現(xiàn),可以發(fā)現(xiàn)主要為testing包的支持,那么testing.T這個類型可以提供哪些測試功能呢?下面解析一下:

    //標(biāo)記失敗但繼續(xù)運(yùn)行該測試
    t.Fail()
    //標(biāo)記失敗并立刻終止該測試
    t.FailNow()

    //打印測試日志
    t.Log("日志記錄...")
    t.Logf("[Error]:%s/n","錯誤原因")

    //t.Log() + t.Fail()
    t.Error("勞資是日志")
    
    //t.Logf() + t.Fail()
    t.Errorf("[Error]:%s/n","錯誤原因")

    //t.Log() + t.FailNow()
    t.Fatal("日志記錄...")
    
    //t.Logf() + t.FailNow()
    t.Fatalf("[Error]:%s/n","錯誤原因")

以上可看到主要是測試運(yùn)行時的日志記錄和錯誤處理功能

三、基準(zhǔn)測試

基準(zhǔn)測試可以測試一段程序的運(yùn)行性能及耗費(fèi) CPU 的程度。Go 語言中提供了基準(zhǔn)測試框架,使用方法類似于單元測試,使用者無須準(zhǔn)備高精度的計時器和各種分析工具,基準(zhǔn)測試本身即可以打印出非常標(biāo)準(zhǔn)的測試報告。

Go進(jìn)行基準(zhǔn)測試和單元測試一樣簡單:

  • 創(chuàng)建一個go文件,在命名文件時以_test.go結(jié)尾,例如: benchmark_test.go

  • 每個測試文件可支持多個測試用例的編寫,每個測試用例函數(shù)需要以Benchmark為前綴,且其只有一個*testing.B指針類型的入?yún)?。例如?/p>

func Benchmark_AXXX( t *testing.B ){}

func Benchmark_BXXX( t *testing.B ){}
  • 運(yùn)行g(shù)o test -v -bench=. benchmark_test.go 執(zhí)行基準(zhǔn)測試
-bench=.表示運(yùn)行 benchmark_test.go 文件里的所有基準(zhǔn)測試,和單元測試中的-run類似

基準(zhǔn)測試用例:

func BenchmarkFibonacci(b *testing.B) {
    b.ReportAllocs() //內(nèi)存開銷
    //b.N為常規(guī)寫法
    for i := 0; i < b.N; i++ {
        Fibonacci(10)
    }
}


//OUTPUT:
goos: darwin
goarch: amd64
BenchmarkFibonacci-4    10000000           204 ns/op           0 B/op          0 allocs/op
--- BENCH: BenchmarkFibonacci-4
PASS
ok      command-line-arguments  2.257s

//10000000 表示測試的次數(shù),也就是 testing.B 結(jié)構(gòu)中提供給程序使用的 N?!?04 ns/op”表示每一個操作耗費(fèi)多少時間(納秒)。

基準(zhǔn)測試原理:

基準(zhǔn)測試框架對一個測試用例的默認(rèn)測試時間是 1 秒。開始測試時,當(dāng)以 Benchmark 開頭的基準(zhǔn)測試用例函數(shù)返回時還不到 1 秒,那么 testing.B 中的 N 值將按 1、2、5、10、20、50……遞增,同時以遞增后的值重新調(diào)用基準(zhǔn)測試用例函數(shù)。

通過-benchtime參數(shù)可以自定義測試時間:

go test -v -bench=. -benchtime=5s benchmark_test.go
//OUTPUT:
goos: darwin
goarch: amd64
BenchmarkFibonacci-4    30000000           204 ns/op           0 B/op          0 allocs/op
--- BENCH: BenchmarkFibonacci-4
PASS
ok      command-line-arguments  6.337s

通過-benchmem參數(shù)以顯示內(nèi)存分配情況

go test -v -bench=BenchmarkFibonacci -benchmem benchmark_test.go
//OUTPUT:
goos: darwin
goarch: amd64
BenchmarkFibonacci-4    10000000           203 ns/op           0 B/op          0 allocs/op
--- BENCH: BenchmarkFibonacci-4
PASS
ok      command-line-arguments  2.251s
//輸出差不多,這個斐波那契例子內(nèi)存分配幾乎忽略不計

控制計時器

有些測試需要一定的啟動和初始化時間,如果從 Benchmark() 函數(shù)開始計時會很大程度上影響測試結(jié)果的精準(zhǔn)性。testing.B 提供了一系列的方法可以方便地控制計時器,從而讓計時器只在需要的區(qū)間進(jìn)行測試。

示例:

func Benchmark_Add_TimerControl(b *testing.B) {
    // 重置計時器
    b.ResetTimer()
    // 停止計時器
    b.StopTimer()
    // 開始計時器
    b.StartTimer()
    var n int
    for i := 0; i < b.N; i++ {
        n++
    }
}

四、性能分析

go提供兩種pprof包來做代碼的性能分析

  • runtime/pprof : 基本性能分析包
  • net/http/pprof : 基于runtime/pprof封裝,并在http端口暴露

1. pprof是什么?

pprof是Go提供的可視化性能分析工具,在性能測試中讀取分析樣本的集合,并生成報告以可視化并幫助分析數(shù)據(jù)。pprof既能生成報告文件,也可以借助graphviz生成web界面。

你能看到什么?

  • CPU Profiling:CPU 分析,按照一定的頻率采集所監(jiān)聽的應(yīng)用程序 CPU(含寄存器)的使用情況,可確定應(yīng)用程序在主動消耗 CPU 周期時花費(fèi)時間的位置
  • Memory Profiling:內(nèi)存分析,在應(yīng)用程序進(jìn)行堆分配時記錄堆棧跟蹤,用于監(jiān)視當(dāng)前和歷史內(nèi)存使用情況,以及檢查內(nèi)存泄漏
  • Block Profiling:阻塞分析,記錄 goroutine 阻塞等待同步(包括定時器通道)的位置
  • Mutex Profiling:互斥鎖分析,報告互斥鎖的競爭情況

2.pprof使用方式

2.1 基于基準(zhǔn)測試生成性能分析文件:
go test -bench=. -benchtime="3s" -cpuprofile=profile_cpu.out

運(yùn)行完成后,會發(fā)現(xiàn)在當(dāng)前目錄生成兩個文件

profile_cpu.out —— 分析報告文件

base.test —— 可執(zhí)行程序

2.2 查看性能分析報告
  • 終端查看
//使用go提供的工具在終端查看
go tool pprof base.test profile_cpu.out

//進(jìn)入終端pprof查看模式
File: base.test
Type: cpu
Time: Jul 4, 2019 at 11:21am (CST)
Duration: 6.55s, Total samples = 6.02s (91.95%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)

//輸入help查看命令集合,可見還是功能還是很強(qiáng)大的。
(pprof) help
  Commands:
    callgrind        Outputs a graph in callgrind format
    comments         Output all profile comments
    disasm           Output assembly listings annotated with samples
    dot              Outputs a graph in DOT format
    eog              Visualize graph through eog
    evince           Visualize graph through evince
    gif              Outputs a graph image in GIF format
    gv               Visualize graph through gv
    kcachegrind      Visualize report in KCachegrind
    list             Output annotated source for functions matching regexp
    pdf              Outputs a graph in PDF format
    peek             Output callers/callees of functions matching regexp
    png              Outputs a graph image in PNG format
    proto            Outputs the profile in compressed protobuf format
    ps               Outputs a graph in PS format
    raw              Outputs a text representation of the raw profile
    svg              Outputs a graph in SVG format
    tags             Outputs all tags in the profile
    text             Outputs top entries in text form
    top              Outputs top entries in text form
    topproto         Outputs top entries in compressed protobuf format
    traces           Outputs all profile samples in text form
    tree             Outputs a text rendering of call graph
    web              Visualize graph through web browser
    weblist          Display annotated source in a web browser
    o/options        List options and their current values
    quit/exit/^D     Exit pprof
    
...

//使用top 5查看前5個耗cpu的調(diào)用
(pprof) top 5
Showing nodes accounting for 5.99s, 99.50% of 6.02s total
Dropped 5 nodes (cum <= 0.03s)
      flat  flat%   sum%        cum   cum%
     5.94s 98.67% 98.67%      5.99s 99.50%  command-line-arguments.Fibonacci
     0.05s  0.83% 99.50%      0.05s  0.83%  runtime.newstack
         0     0% 99.50%      5.99s 99.50%  command-line-arguments.BenchmarkFibonacci
         0     0% 99.50%      5.99s 99.50%  testing.(*B).launch
         0     0% 99.50%      5.99s 99.50%  testing.(*B).runN

...
  • web查看
//先安裝[graphviz](http://www.graphviz.org/download/)
//加上--web
go tool pprof --web base.test profile_cpu.out

//此方式會生成一個.svg格式的文件,可以用任何支持.svg的軟件打開
  • pdf輸出
//加上-pdf選項,并把數(shù)據(jù)重定向到新的pdf格式文件即可
go tool pprof base.test -pdf profile_cpu.out > profile_cpu.pdf

3. 服務(wù)版的pprof性能分析

要使用服務(wù)版的pprof,只需在啟動服務(wù)的main中引入相關(guān)包,啟動服務(wù)即可。

    "net/http"
    _ "net/http/pprof"

運(yùn)行服務(wù)時,你的 HTTP 服務(wù)會多出 /debug/pprof 這個訪問路徑,用于查看服務(wù)器版的性能分析報告,例如:訪問http://{hostname}:{port}/debug/pprof/

可以通過訪問各自類型的性能分析頁面了解服務(wù)的總體情況:
  • cpu: http://{hostname}:{port}/debug/pprof/profile,默認(rèn)進(jìn)行 30s 的 CPU Profiling,得到一個分析用的 profile 文件
  • block:http://{hostname}:{port}/debug/pprof/block,查看導(dǎo)致阻塞同步的堆棧跟蹤
  • goroutine:http://{hostname}:{port}/debug/pprof/goroutine,查看當(dāng)前所有運(yùn)行的 goroutines 堆棧跟蹤
  • heap: http://{hostname}:{port}/debug/pprof/heap,查看活動對象的內(nèi)存分配情況
  • mutex:http://{hostname}:{port}/debug/pprof/mutex,查看導(dǎo)致互斥鎖的競爭持有者的堆棧跟蹤
  • threadcreate:http://{hostname}:{port}/debug/pprof/threadcreate,查看創(chuàng)建新OS線程的堆棧跟蹤
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容