Go test基礎(chǔ)用法大全

go 語(yǔ)言的 test 命令有很多參數(shù),怎么利用 test 命令和它提供的參數(shù),又能做到什么?本文做了詳細(xì)解讀。
當(dāng)直接使用IDE進(jìn)行單元測(cè)試時(shí),有沒(méi)有好奇它時(shí)如何實(shí)現(xiàn)的?比如GoLand寫的測(cè)試用例。
go?build?main.go
./main?-log_dir="/data"????//當(dāng)前目錄作為日志輸出目錄
但在go test的時(shí)候,如何指定這個(gè)參數(shù)了?
Test
調(diào)查發(fā)現(xiàn),發(fā)現(xiàn)go test也可以生成可執(zhí)行文件。需要使用-c來(lái)指定。示例如下:
go?test?-c?param_test_dir???//最后一個(gè)參數(shù)是待測(cè)試的目錄
執(zhí)行后就會(huì)發(fā)現(xiàn):這樣的做法,會(huì)運(yùn)行所有的Test用例。如何僅僅執(zhí)行某一個(gè)測(cè)試用例了(編譯器到底是如何做到的?)。
這里有另一個(gè)屬性-run,用來(lái)指定執(zhí)行的測(cè)試用例的匹配模式。舉個(gè)例子:
func?TestGetRootLogger(t?*testing.T)?{
?writeLog("測(cè)試")
}
func?TestGetRootLogger2(t?*testing.T)?{
?writeLog("測(cè)試?2")
}
當(dāng)我在命令行明確匹配執(zhí)行Logger2,運(yùn)行的時(shí)候確實(shí)僅僅執(zhí)行該測(cè)試用例
go?test?-v?-run?Logger2?./util/?????//-v?表示?verbose,輸出相信信息
但是,我發(fā)現(xiàn),在指定了c參數(shù)之后,run參數(shù)無(wú)法生效!這樣的話,還真是沒(méi)有好的辦法來(lái)處理這種情況。
option
-timeout
默認(rèn)go test運(yùn)行超過(guò)10m會(huì)發(fā)生panic。如果需要運(yùn)行更長(zhǎng)的時(shí)間,需要明確指定。將timeout指定為 0,用于忽略時(shí)間限制。
nohup?go?test?-v?-timeout?0?-run?TestGetRange?.?>?log.txt
使用map的測(cè)試
可以結(jié)合使用閉包,設(shè)置期望值,來(lái)寫測(cè)試用例。Run函數(shù)內(nèi)部是阻塞的,所以TestSum方法依次執(zhí)行測(cè)試。
同時(shí)testSumFunc返回了test方法使用了閉包的特性,對(duì)返回函數(shù)內(nèi)部的值是無(wú)法確定的。
func?TestSum(t?*testing.T)?{
?t.Run("A",?testSumFunc([]int{1,?2,?3},?7))
?t.Run("B",?testSumFunc([]int{2,?3,?4},?8))
}
func?Sum(numbers?[]int)?int?{
?total?:=?0
?for?_,?v?:=?range?numbers?{
??total?+=?v
?}
?return?total
}
func?testSumFunc(numbers?[]int,?expected?int)?func(t?*testing.T)?{
?return?func(t?*testing.T)?{
??actual?:=?Sum(numbers)
??if?actual?!=?expected?{
???t.Error(fmt.Sprintf("Expected?the?sum?of?%v?to?be?%d?but?instead?got?%d!",?numbers,?expected,?actual))
??}
?}
}
Main
非常簡(jiǎn)單,看如下示例。這樣在執(zhí)行任何test case時(shí)都首先執(zhí)行準(zhǔn)備,在測(cè)試用例執(zhí)行完畢后,會(huì)運(yùn)行清理工作。需要特別說(shuō)明的是:flag.Parse()以及os.Exit(m.Run())是不可缺少的兩步。
func?TestMain(m?*testing.M)?{
????//準(zhǔn)備工作
?fmt.Println("start?prepare")
?flag.Parse()
?exitCode?:=?m.Run()
????
????//清理工作
?fmt.Println("prepare?to?clean")
?
?os.Exit(exitCode)
}
性能測(cè)試pprof
定位服務(wù)是否存在資源泄漏或者濫用API的行為,光靠review代碼是不行的,最好能借助工具。
Profile
引用?godoc for pprof?描述:
A Profile is a collection of stack traces showing the call sequences that led to instances of a particular event, such as allocation. Packages can create and maintain their own profiles; the most common use is for tracking resources that must be explicitly closed, such as files or network connections.
性能測(cè)試涉及如下方面:
CPU Profiling:CPU分析,按照一定的頻率采集所監(jiān)聽(tīng)的應(yīng)用程序CPU(含寄存器)的使用情況,可確定應(yīng)用程序在主動(dòng)消耗CPU?周期時(shí)花費(fèi)時(shí)間的位置Memory Profiling:內(nèi)存分析,在應(yīng)用程序進(jìn)行堆分配時(shí)記錄堆棧跟蹤,用于監(jiān)視當(dāng)前和歷史內(nèi)存使用情況,以及檢查內(nèi)存泄漏Block Profiling:阻塞分析,記錄?goroutine?阻塞等待同步(包括定時(shí)器通道)的位置Mutex Profiling:互斥鎖分析,報(bào)告互斥鎖的競(jìng)爭(zhēng)情況
在程序中引入如下包,便可以通過(guò) web 方式查看性能情況,訪問(wèn)的路徑為:/debug/pprof/,該路徑下會(huì)顯示多個(gè)查看項(xiàng)。該路徑下還有其他子頁(yè)面。
_?"net/http/pprof"
關(guān)于/debug/pprof/下的子頁(yè)面:
$HOST/debug/pprof/profile$HOST/debug/pprof/block$HOST/debug/pprof/goroutine$HOST/debug/pprof/heap$HOST/debug/pprof/mutex$HOST/debug/pprof/threadcreate
在終端查看性能
只要服務(wù)器在啟動(dòng)時(shí),引入pprof包,便可在終端獲取profile文件。如下所示:
pprof?-seconds=10?http://192.168.77.77:3900/debug/pprof/profile
如果獲取到cpu.prof文件,可以通過(guò)如下命令可視化查看運(yùn)行結(jié)果,這是另一種格式的火焰圖,也是挺帥的:
##?通過(guò)在瀏覽器中?localhost:1313?可以在?web?端查看
##?
pprof?-http=:1313?cpu.prof
##?或直接在終端查看
go?tool?pprof?cpu.prof
$?web?|?top
Benchmark測(cè)試
基本用法:
func?BenchmarkBadgeRelationMapper_GetCountByUid(b?*testing.B)?{
?count?:=?0
?for?i?:=?0;?i???count++
?}
?fmt.Println("total:",?count)
}
bench測(cè)試輸出結(jié)果,函數(shù)體被重復(fù)執(zhí)行了 6 次,并對(duì)b.N的值做了調(diào)整:
total:?1
total:?100
total:?10000
total:?1000000
total:?100000000
total:?1000000000
1000000000??????????0.598?ns/op
并發(fā)用法:
func?BenchmarkBadgeRelationMapper_GetCountByUid(b?*testing.B)?{
?count?:=?0
?b.RunParallel(func(pb?*testing.PB)?{
??for?pb.Next()?{
???count++
??}
?})
?fmt.Println("total:",?count)
}
輸出的結(jié)果:
total:?1
total:?100
total:?6336
total:?306207
total:?34221963
total:?129821900
378966799??????????2.94?ns/op
在并行用法中,b.N被RunParallel接管。
簡(jiǎn)單分析一下源碼
核心思路在于Next方法,通過(guò)atomic.AddUint64并發(fā)安全的操作pb.globalN,pb.cache用來(lái)存儲(chǔ)該goroutine執(zhí)行的次數(shù)。當(dāng)某個(gè)goroutine計(jì)算到pb.bN<=n<=pb.bN+pb.grain時(shí),雖然程序迭代次數(shù)已經(jīng)完全超過(guò)b.N,但還是會(huì)讓它繼續(xù)執(zhí)行。
//?Next?reports?whether?there?are?more?iterations?to?execute.
func?(pb?*PB)?Next()?bool?{
?if?pb.cache?==?0?{
??n?:=?atomic.AddUint64(pb.globalN,?pb.grain)
??if?n?<=?pb.bN?{
???pb.cache?=?pb.grain
??}?else?if?n????pb.cache?=?pb.bN?+?pb.grain?-?n
??}?else?{
???return?false
??}
?}
?pb.cache--
?return?true
}
regular expression
先列舉參考的example。在我們要運(yùn)行特定case時(shí),可以通過(guò)指定正則表達(dá)式來(lái)實(shí)現(xiàn):
//?-bench?takes?a?regular?expression?that?matches?the?names?of?the?benchmarks?you?want?to?run
go?test?-bench=.?./examples/fib/
//?-run?flag?with?a?regex?that?matches?nothing
go?test?-run=^$
關(guān)于如何運(yùn)行Benchmark測(cè)試,默認(rèn)執(zhí)行go test并不會(huì)執(zhí)行Benchmark,需要在命令行明確加上-bench=標(biāo)記,它接受一個(gè)表達(dá)式作為參數(shù),匹配基準(zhǔn)測(cè)試的函數(shù),. 表示運(yùn)行所有基準(zhǔn)測(cè)試。
go?test?-bench=.
//?明確指定要運(yùn)行哪個(gè)測(cè)試,傳遞一個(gè)正則表達(dá)式給?run?屬性,XXX=BenchmarkReceiveGift_GetGiftReceiveList
go?test?-run=XXX?-bench=.
默認(rèn)情況下,benchmark最小運(yùn)行時(shí)長(zhǎng)為1s。如果benchmark函數(shù)執(zhí)行返回,但1s的時(shí)間還沒(méi)有結(jié)束,b.N會(huì)根據(jù)某種機(jī)制依次遞增??梢酝ㄟ^(guò)參數(shù)-benchtime=20s來(lái)改變這種行為。
還有一個(gè)參數(shù):benchmem??梢蕴峁┟看尾僮鞣峙鋬?nèi)存的次數(shù),以及每次操作分配的字節(jié)數(shù)。
go?test?-bench=Fib40?-benchtime=20s
Run Example
獲取線上的pprof數(shù)據(jù)到本地,這里是另一個(gè)工具:
go-torch?-u?http://192.168.77.77:3900/debug/pprof/profile?-t?10
在Go 代碼調(diào)優(yōu)利器-火焰圖這篇文章中,對(duì)例子介紹的挺精彩的。
##?對(duì)函數(shù)?GetGiftReceiveList?進(jìn)行?Benchmark?測(cè)試?因?yàn)橹幌雺簻y(cè)?GetGiftReceiveList?這個(gè)函數(shù)
##?所以指定了?run?參數(shù)
go?test?-bench?.?-run=GetGiftReceiveList?-benchmem?-cpuprofile?prof.cpu
##?其中?present.test?是壓測(cè)的二進(jìn)制文件,prof.cpu?也是生產(chǎn)的文件
##?(pprof)?top10
##?(pprof)?list?Marshal?單獨(dú)查看這個(gè)函數(shù)的耗時(shí),這里應(yīng)該是正則匹配的
go?tool?pprof?present.test?prof.cpu
##?查看內(nèi)存使用情況
go?test?-bench?.?-benchmem?-memprofile?prof.mem
go?tool?pprof?--alloc_objects??present.test?prof.mem
覆蓋率
跟執(zhí)行go test不同的是,需要多加一個(gè)參數(shù)-coverprofile, 所以完整的命令:
go?test?-v?-coverprofile=c.out
生成報(bào)告有 go 為我們提供的工具,使用
go?tool?cover?-html=c.out?-o=tag.html
即可生成一個(gè)名字為 tag.html 的 HTML 格式的測(cè)試覆蓋率報(bào)告,這里有詳細(xì)的信息告訴我們哪一行代碼測(cè)試到了,哪一行代碼沒(méi)有測(cè)試到。
火焰圖
學(xué)習(xí)了解火焰圖,分析函數(shù)調(diào)用棧的信息。主要是相關(guān)的工具:
##?tool1
git?clone?https://github.com/brendangregg/FlameGraph.git
cp?flamegraph.pl?/usr/local/bin
flamegraph.pl?-h
go?get?-v?github.com/uber/go-torch
go-torch?-h文章轉(zhuǎn)載:Go開(kāi)發(fā)大全
(版權(quán)歸原作者所有,侵刪)

點(diǎn)擊下方“閱讀原文”查看更多
