Go怎么寫測試用例
- 開發程序其中很重要的一點就是測試,我們如何保證代碼質量,如何保證每個函數都是可運行的,運行結果是正確的,又如何保證寫出來的代碼性能是好的,我們知道單元測試的重點在于發現程序設計或實現的邏輯錯誤,使問題及早暴露,便于問題的定位解決,而性能測試的重點在于發現程序設計的一些問題,讓線上的程序能夠高并發的情況下還能夠保持穩定。
- Go語言中自帶又一個輕量級的測試框架testing和自帶的go test命令來實現單元測試和性能測試,testing框架及其他語言的中的測試框架類似,你可以給予這個框架寫真對相應函數的測試用例,也可以基于該框架寫相應的壓力測試用例
如何編寫測試用例
- 由于go test命令只能在一個相應的目錄下執行所有文件,所以我們先創建一個項目目錄gotest,這樣我們所有的代碼和測試代碼都在這個目錄下。我們在該目錄下創建兩個文件:gotest.go和gotest_test.go
- gotest.go這個文件里面我們是創建了一個包
package gotest
import "errors"
func Division(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除數不能為0")
}
return a / b, nil
}
- gotest_test.go是我們的單元測試文件,單元測試文件必須遵守下面原則:
- 文件名必須是_test.go結尾,這樣執行go test的時候才會執行到相應的代碼
- 必須import testing這個包
- 所有測試函數必須是Test開頭
- 測試用例會按照源代碼中寫的順序依次執行
- 測試函數的參數是testing.T,我們可以使用該類型來記錄錯誤或者是測試狀態
- 測試函數格式:*func TestXxx(t testing.T),Xxx部分可以任意字母數字的組合,但是首字母不能是小寫
- 函數中通過調用testing.T 的Error,Errorf,FailNow,Fatal,FatalIf方法,說明測試不通過,調用Log方法來記錄測試信息
package gotest
import (
"testing"
)
func Test_Division_1(t *testing.T) {
if i, e := Division(6, 2); i != 3 || e != nil { //try a unit test on function
t.Error("除法函數測試沒通過") // 如果不是如預期的那么就報錯
} else {
t.Log("第一個測試通過了") //記錄一些你期望記錄的信息
}
}
func Test_Division_2(t *testing.T) {
t.Error("就是不通過")
}
當我們進入到gotest目錄后執行go test會答應以下信息
--- FAIL: Test_Division_2 (0.00s)
gotest_test.go:16: 就是不通過
FAIL
exit status 1
FAIL web/gotest 0.006s
由于第二個函數我們直接寫死了t.Error,所以測試不通過。第一個函數沒有打印信息是因為默認情況下go test不會顯示測試通過的信息,我們可以使用帶參數go test -v,這樣就會顯示如下信息:
=== RUN Test_Division_1
--- PASS: Test_Division_1 (0.00s)
gotest_test.go:11: 第一個測試通過了
=== RUN Test_Division_2
--- FAIL: Test_Division_2 (0.00s)
gotest_test.go:16: 就是不通過
FAIL
exit status 1
FAIL web/gotest 0.006s
- 我們將第二個函數做如下修改
func Test_Division_2(t *testing.T) {
if _, e := Division(6, 0); e == nil { //try a unit test on function
t.Error("Division did not work as expected.") // 如果不是如預期的那么就報錯
} else {
t.Log("one test passed.", e) //記錄一些你期望記錄的信息
}
}
再執行go test -v,就會顯示:
=== RUN Test_Division_1
--- PASS: Test_Division_1 (0.00 seconds)
gotest_test.go:11: 第一個測試通過了
=== RUN Test_Division_2
--- PASS: Test_Division_2 (0.00 seconds)
gotest_test.go:20: one test passed. 除數不能為0
PASS
ok gotest 0.013s
如何編寫壓力測試
壓力測試用來檢測函數的性能的,和編寫單元功能的測試的方法類似,壓力測試必須注意以下幾點:
- 壓力測試用例必須遵循如下格式,其中XXX可以是任意字母數字組合,但是首字母不能是小寫字母
func BenchmarkXXX(b *testing.B) { ... }
- go test不會默認執行壓力測試的函數,如果要執行壓力測試需要帶上參數-test.bench,語法:-test.bench="test_name_regex",例如go test.bench="."* 表示測試全部的壓力測試函數
- 在壓力測試用例中,請記得在循環體內使用testing.B.N,以使測試可以正常運行
- 文件名也必須是以_test.go結尾
新建壓力測試文件webbench_test.go,代碼如下所示:
func Benchmark_Division(b *testing.B) {
for i := 0; i < b.N; i++ { //use b.N for looping
Division(4, 5)
}
}
func Benchmark_TimeConsumingFunction(b *testing.B) {
b.StopTimer() //調用該函數停止壓力測試的時間計數
//做一些初始化的工作,例如讀取文件數據,數據庫連接之類的,
//這樣這些時間不影響我們測試函數本身的性能
b.StartTimer() //重新開始時間
for i := 0; i < b.N; i++ {
Division(4, 5)
}
}
執行命令go test -run="webbench_test.go" -test.bench="."*進行webbench_test.go文件的全部壓力測試,可以得到以下結果:
goarch: amd64
pkg: web/gotest
Benchmark_Division-4 2000000000 0.36 ns/op
Benchmark_TimeConsumingFunction-4 2000000000 0.36 ns/op
PASS
ok web/gotest 1.500s
上面結果顯示了我們只執行了壓力測試函數,其中函數Benchmark_Division我們執行了2000000000次,每次執行平均時間為0.36ns,函數Benchmark_TimeConsumingFunction我們執行了2000000000次,每次執行平均時間為0.36ns,總共時間花了1.5s
執行命令:go test -run="webbench_test.go" -test.bench="Benchmark_TimeConsumingFunction"用于單獨壓力測試函數Benchmark_TimeConsumingFunction,得到以下結果:
goos: darwin
goarch: amd64
pkg: web/gotest
Benchmark_TimeConsumingFunction-4 2000000000 0.35 ns/op
PASS
ok web/gotest 0.737s
顯示該函數執行了2000000000次,平均每次執行時間為0.35ns,總共執行時間為0.737s