Go 單元測試和性能測試

測試對於互聯網應用軟件開發來說非常重要,它對軟件可靠性保證具有重要意義,通過測試能夠儘可能發現並改正軟件中的錯誤,提高軟件質量。

這裏我們主要講解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.TError, 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...

links

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章