Golang UnitTest單元測試
Golang UnitTest單元測試
單元測試是程式開發者適用一段程式碼來驗證另外一段程式碼寫的是否符合預期的一種相對高效的自我測試方法。
對於程式開發來說,經過長期的積累和方法總結,單元測試是一種比較好的開發程式驗證方式,但是單元測試卻是在正真的軟體開發之外要寫的額外的程式碼,而且編寫和管理維護都有比較大的成本。但是單元測試確實能夠提高程式開發的質量,所以基本上各種語言都有相應的單元測試框架來支援更為方便編寫和管理單元測試。
golang也是一樣,不過golang的測試框架的支援是自生就提供了,不像C/C++,php等語言的單元測試框架需要第三方提供和安裝。golang你在安裝了其開發編譯環境之後就自然包含了單元測試的框架。今天這裡就來簡單說明一下golang的單元測試框架的適用。
golang UnitTest的書寫規範和簡單適用
規範
Golang單元測試對檔名和方法名,引數都有很嚴格的要求。
例如:
1. 單元測試檔名必須以xxx_test.go命名 2. 方法必須是TestXxx開頭 3. 方法引數必須 t *testing.T 4. 測試檔案和被測試檔案必須在一個包中
示例
這是我在我的小專案 設計模式golang實現 中的一個 例子 ,另外這裡說明一下:一般情況下單元測試檔案和被測試檔案是放到同一個目錄的。
import ( "fmt" "testing" ) func TestFactory(t *testing.T) { shapeFactory := ShapeFactory{} rectangle := shapeFactory.Produce("rectangle") if rectangle != nil { rectangle.Draw() } errshape := shapeFactory.Produce("cricle") if errshape == nil { fmt.Println("errshape") } }
這樣的來編寫單元測試是不是很方便呀!
執行單元測試
這裡也可以參考一下我的小專案 設計模式golang實現 中的 Makefile 執行非常簡單。
go test -v creational/01_factory/
這裡加v引數是為了輸出更多的資訊, 可以把print的資訊和一些測試資訊都能比較詳細的輸出
golang單元測試進階
go test引數幫助
這裡給大家介紹與喜愛go test的幫助適用,golang這方面做的還是非常不錯的,主要涉及兩個命令:
命令1:go help test
$ go help test usage: go test [build/test flags] [packages] [build/test flags & test binary flags] 'Go test' automates testing the packages named by the import paths. It prints a summary of the test results in the format: okarchive/tar0.011s FAIL archive/zip0.022s okcompress/gzip 0.033s ... ...
命令2: go help packages
一般情況下整個命令用的比較少。
$ go help packages Many commands apply to a set of packages: go action [packages] Usually, [packages] is a list of import paths.
命令3:go help testflag
$ go help testflag The 'go test' command takes both flags that apply to 'go test' itself and flags that apply to the resulting test binary. ...
下面是常用的一些flags
-test.v : 是否輸出全部的單元測試用例(不管成功或者失敗),預設沒有加上,所以只輸出失敗的單元測試用例。
-test.run pattern: 只跑哪些單元測試用例
-test.bench patten: 只跑那些效能測試用例
-test.benchmem : 是否在效能測試的時候輸出記憶體情況
-test.benchtime t : 效能測試執行的時間,預設是1s
-test.cpuprofile cpu.out : 是否輸出cpu效能分析檔案
-test.memprofile mem.out : 是否輸出記憶體效能分析檔案
-test.blockprofile block.out : 是否輸出內部goroutine阻塞的效能分析檔案
golang & vs code
使用過那麼多編輯器之後發現只有vim和vs code最好用,vs code在圖形化編碼工具中目前我認為是最好的,vim是命令列編輯器中最好的。目前也是使用vs code來編寫markdown檔案的。這裡不是重點,重點看下面的。
安裝go外掛
啟動vscode選擇外掛->搜go選擇Go for Visual Studio Code外掛點選安裝即可。如圖:
配置vscode
settins.json 基本上不需要配置,用預設值就可以了。如需調整 檔案 –>首選項 –>設定 輸入go即可檢視go相關配置,以下是我的配置,不過一般情況下無需修改配置,也可以自己多設定一點
"go.goroot": "C:\\Go", "go.gopath": "${workspaceRoot};D:\\go4\\go_thirdpaty", "go.buildFlags": [], "go.lintFlags": [], "go.coverOnSave": false, "go.useCodeSnippetsOnFunctionSuggest": false, "go.formatTool": "goreturns", "go.gocodeAutoBuild": false, "go.toolsGopath": "D:\\go4\\tools",
vscode中怎麼開發單元測試
自動生成ut檔案
// datacal.go package main func add_num(a int, b int) int { return a + b }
例如有上面的檔案,在vs安裝了go工具之後,我們在add_num整個函式內點選右鍵,選擇為該函式生成單元測試即可,如下圖
第一次適用的時候會安裝相應的一個小元件,安裝之後既可以適用了,上面的程式碼用該方式自動生成的單元測試檔案如下:
// datacal.go package main import "testing" func Test_add_num(t *testing.T) { type args struct { a int b int } tests := []struct { name string args args want int }{ // TODO: Add test cases. {"1 + 1", args{1, 1}, 2}, // 自己新增的測試用例資料 {"1 + 2", args{1, 2}, 3}, // 自己新增的測試用例資料 {"1 + 3", args{1, 3}, 4}, // 自己新增的測試用例資料 } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := add_num(tt.args.a, tt.args.b); got != tt.want { t.Errorf("add_num() = %v, want %v", got, tt.want) } }) } }
執行測試
helightxu@helightxu-NB0 MINGW64 /d/code_dev/heliht_github/helight_code/go_code/go_test2 (master) $ go test -v === RUNTest_add_num === RUNTest_add_num/1_+_1 === RUNTest_add_num/1_+_2 === RUNTest_add_num/1_+_3 --- PASS: Test_add_num (0.00s) --- PASS: Test_add_num/1_+_1 (0.00s) --- PASS: Test_add_num/1_+_2 (0.00s) --- PASS: Test_add_num/1_+_3 (0.00s) PASS ok_/d_/code_dev/heliht_github/helight_code/go_code/go_test20.315s
這裡我們也可以這樣來跑:指定函式執行,很多時候測試用例多的時候修改了一個就想執行這一個,這個就很有用了。
$ go test -run add_num -v === RUNTest_add_num === RUNTest_add_num/1_+_1 === RUNTest_add_num/1_+_2 === RUNTest_add_num/1_+_3 --- PASS: Test_add_num (0.00s) --- PASS: Test_add_num/1_+_1 (0.00s) --- PASS: Test_add_num/1_+_2 (0.00s) --- PASS: Test_add_num/1_+_3 (0.00s) PASS ok_/d_/code_dev/heliht_github/helight_code/go_code/go_test20.265s
單元測試關鍵點
測試資料構造
單元測試的一個重點就是測試資料的構造,在測試資料構造時要考慮這樣幾個方面:
- 正常輸入,整個必不可少,至少驗證函式的正常邏輯是否通過
- 邊界輸入,這個主要驗證在極端情況下的輸入,函式是否在有相應的容錯處理
- 非法輸入,對於一些非正常輸入,我們要看函式是否處理,會不引起函式的奔潰和資料洩露等問題
- 白盒覆蓋,白盒覆蓋就要設計了,要設計一些用力,能夠覆蓋到函式的所有程式碼,這裡主要考慮:語句覆蓋、條件覆蓋、分支覆蓋、分支/條件覆蓋、條件組合覆蓋。
編寫原則
單元測試是要寫額外的程式碼的,這對開發同學的也是一個不小的工作負擔,在一些專案中,我們合理的評估單元測試的編寫,我認為我們不能走極端,當然理論上來說全寫肯定時好的,但是從成本,效率上來說我們必須做出權衡。所以這裡給出一些衡量的原則
1. 優先編寫核心元件和邏輯模組的測試用例
2. 邏輯類似的元件如果存在多個,優先編寫其中一種邏輯元件的測試用例
3. 發現Bug時一定先編寫測試用例進行Debug
4. 關鍵util工具類要編寫測試用例,這些util工具適用的很頻繁,所以這個原則也叫做熱點原則,和第1點相呼應。
5. 測試使用者應該獨立,一個檔案對應一個,而且不同的測試用例之間不要互相依賴。
6. 測試用例的保持更新。
Benchmark測試
介紹
最後這裡簡單也說以下go中benchmark的測試,這個先對比較簡單,主要針對cpu型別或者記憶體型別的計算進行效能測試的一種方式,有時也用在io上,但是io在單元測試中是比較敏感的,一般磁碟io和遠端網路io很少做benchmark測試。
benchmark的測試函式形式時這樣的:
1. 函式是以Benchmark開頭
2. 引數是*testing.B和普通單元測試的不一樣哈(t *testing.T))。
func BenchmarkXxx(b *testing.B)
測試用例示例
在上面的例子基礎上增加一個benchmark的測試:
func Benchmark_add_num(b *testing.B) { sum := 0 for i := 0; i < b.N; i++ { sum = add_num(1343, 434) } b.Log(sum) }
這裡可以看到b.N這個引數,這個資料是go自己預測的一個值,目標是有足夠的測試來觀察。
執行benchmark測試
$ go test -v -run="none" -bench=. -benchmem goos: windows goarch: 386 Benchmark_add_num-420000000000.90 ns/op0 B/op0 allocs/op --- BENCH: Benchmark_add_num-4 datacal_test.go:37: 1777 datacal_test.go:37: 1777 datacal_test.go:37: 1777 datacal_test.go:37: 1777 datacal_test.go:37: 1777 datacal_test.go:37: 1777 PASS ok_/d_/code_dev/heliht_github/helight_code/go_code/go_test23.870s
這不允許單元測試,執行所有的基準測試,-bench可以指定函式名,支援正則。
-benchmem 表示分配記憶體的次數和位元組數
其它
這裡沒有介紹golang的cover,記憶體,cpu分析,還有mock等測試方式,留作以後再介紹。想在下一篇中寫寫mock測試,尤其golang開發中的。
參考連結
go testing: http://docs.studygolang.com/pkg/testing/
https://github.com/golang/go/wiki