測試對於互聯網應用軟件開發來說非常重要,它對軟件可靠性保證具有重要意義,通過測試能夠儘可能發現並改正軟件中的錯誤,提高軟件質量。
這裏我們主要講解Go語言如何實現單元測試和性能測試。
go語言中自帶有一個輕量級的測試框架testing和自帶的go test命令來實現單元測試和性能測試,testing框架和其他語言中的測試框架類似,你可以基於這個框架寫針對相應函數的測試用例,也可以基於該框架寫相應的壓力測試用例,那麼接下來讓我們一一來看一下怎麼寫。
單元測試
創建目錄test,在目錄下創建add.go、add_test.go兩個文件,add_test.go爲單元測試文件。
add_test.go
package test
import "testing"
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum == 3 {
t.Log("the result is ok")
} else {
t.Fatal("the result is wrong")
}
}
func TestAdd1(t *testing.T) {
t.Error("the result is error")
}
add.go
package test
func Add(a, b int) int {
return a + b
}
然後在項目目錄下運行go test -v
就可以看到測試結果了
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
add_test.go:8: the result is ok
=== RUN TestAdd1
--- FAIL: TestAdd1 (0.00s)
add_test.go:14: the result is error
FAIL
exit status 1
FAIL _/D_/gopath/src/ados/test 0.419s
如果看到PASS字樣證明測試通過,FAIL字樣表示測試失敗。
使用testing庫的測試框架需要遵循以下幾個規則如下:
- 文件名必須是
_test.go
結尾的,這樣在執行go test
的時候纔會執行到相應的代碼 - 你必須import
testing
這個包 - 所有的測試用例函數必須是
Test
開頭 - 測試用例會按照源代碼中寫的順序依次執行
- 測試函數
TestXxx()
的參數是testing.T
,我們可以使用該類型來記錄錯誤或者是測試狀態 - 測試格式:
func TestXxx (t *testing.T)
,Xxx
部分可以爲任意的字母數字的組合,但是首字母不能是小寫字母[a-z],例如Testintdiv
是錯誤的函數名。 - 函數中通過調用
testing.T
的Error
,Errorf
,FailNow
,Fatal
,FatalIf
方法,說明測試不通過,調用Log
方法用來記錄測試的信息。
性能測試或壓力測試
壓力測試用來檢測函數(方法)的性能,和編寫單元功能測試的方法類似,此處不再贅述,但需要注意以下幾點:
-
壓力測試用例必須遵循如下格式,其中XXX可以是任意字母數字的組合,但是首字母不能是小寫字母
func BenchmarkXXX(b *testing.B) { ... }
-
go test
不會默認執行壓力測試的函數,如果要執行壓力測試需要帶上參數-test.bench
,語法:-test.bench="test_name_regex"
,例如go test -test.bench=".*"
表示測試全部的壓力測試函數 - 在壓力測試用例中,請記得在循環體內使用
testing.B.N
,以使測試可以正常的運行 - 文件名也必須以
_test.go
結尾
在test目錄下創建 reflect_test.go
package test
import (
"reflect"
"testing"
)
type Student struct {
Name string
Age int
Class string
Score int
}
func BenchmarkReflect_New(b *testing.B) {
var s *Student
sv := reflect.TypeOf(Student{})
b.ResetTimer()
for i := 0; i < b.N; i++ {
sn := reflect.New(sv)
s, _ = sn.Interface().(*Student)
}
_ = s
}
func BenchmarkDirect_New(b *testing.B) {
var s *Student
b.ResetTimer()
for i := 0; i < b.N; i++ {
s = new(Student)
}
_ = s
}
func BenchmarkReflect_Set(b *testing.B) {
var s *Student
sv := reflect.TypeOf(Student{})
b.ResetTimer()
for i := 0; i < b.N; i++ {
sn := reflect.New(sv)
s = sn.Interface().(*Student)
s.Name = "Jerry"
s.Age = 18
s.Class = "20005"
s.Score = 100
}
}
func BenchmarkReflect_SetFieldByName(b *testing.B) {
sv := reflect.TypeOf(Student{})
b.ResetTimer()
for i := 0; i < b.N; i++ {
sn := reflect.New(sv).Elem()
sn.FieldByName("Name").SetString("Jerry")
sn.FieldByName("Age").SetInt(18)
sn.FieldByName("Class").SetString("20005")
sn.FieldByName("Score").SetInt(100)
}
}
func BenchmarkReflect_SetFieldByIndex(b *testing.B) {
sv := reflect.TypeOf(Student{})
b.ResetTimer()
for i := 0; i < b.N; i++ {
sn := reflect.New(sv).Elem()
sn.Field(0).SetString("Jerry")
sn.Field(1).SetInt(18)
sn.Field(2).SetString("20005")
sn.Field(3).SetInt(100)
}
}
func BenchmarkDirect_Set(b *testing.B) {
var s *Student
b.ResetTimer()
for i := 0; i < b.N; i++ {
s = new(Student)
s.Name = "Jerry"
s.Age = 18
s.Class = "20005"
s.Score = 100
}
}
在test目錄下,執行:
go test reflect_test.go -test.bench=".*"
結果如下
goos: windows
goarch: amd64
BenchmarkReflect_New-4 20000000 84.9 ns/op
BenchmarkDirect_New-4 30000000 50.6 ns/op
BenchmarkReflect_Set-4 20000000 89.9 ns/op
BenchmarkReflect_SetFieldByName-4 3000000 552 ns/op
BenchmarkReflect_SetFieldByIndex-4 10000000 132 ns/op
BenchmarkDirect_Set-4 30000000 53.0 ns/op
PASS
ok command-line-arguments 10.982s
上面的結果顯示我們沒有執行任何TestXXX
的單元測試函數,顯示的結果只執行了壓力測試函數,以第三行爲例
BenchmarkReflect_New 函數執行了20000000次,每次的執行平均時間是84.9納秒。最後一行 command-line-arguments 10.982s,代表總的執行時間爲 10.982s。
如果只想對某個函數測試,以BenchmarkReflect_New 爲例,執行命令
go test reflect_test.go -test.bench="BenchmarkReflect_New"
結果爲:
goos: windows
goarch: amd64
BenchmarkReflect_New-4 20000000 84.9 ns/op
PASS
ok command-line-arguments 2.490s
如果測試整個目錄下的所有測試執行:
go test -test.bench=".*"
如果想顯示內存分配的次數和大小添加 -benchmem
go test reflect_test.go -benchmem -test.bench=".*"
goos: windows
goarch: amd64
BenchmarkReflect_New-4 20000000 88.3 ns/op 48 B/op 1 allocs/op
BenchmarkDirect_New-4 30000000 53.8 ns/op 48 B/op 1 allocs/op
BenchmarkReflect_Set-4 20000000 90.9 ns/op 48 B/op 1 allocs/op
BenchmarkReflect_SetFieldByName-4 3000000 564 ns/op 80 B/op 5 allocs/op
BenchmarkReflect_SetFieldByIndex-4 10000000 135 ns/op 48 B/op 1 allocs/op
BenchmarkDirect_Set-4 30000000 52.4 ns/op 48 B/op 1 allocs/op
PASS
ok command-line-arguments 12.955s
後兩列代表分配的內存大小和次數(48 B/op 1 allocs/op)
推薦gotests
它是編寫Go測試的一個Golang命令行工具,可以根據目標源文件的函數和方法簽名生成表驅動的測試。將自動導入測試文件中的任何新依賴項。
參考:
https://studygolang.com/stati...
https://www.cnblogs.com/yjf51...