golang 之單元測試


測試

  測試是自動化測試的簡稱,即編寫簡單的程序來確保程序(產品代碼)在測試中針對特定輸入產生預期的輸出。Go的測試方法看上去相對比較低級,它依賴於命令go test和一些能用go test運行的測試函數的編寫約定

go test 工具

  go test 子命令是Go語言包的測試驅動程序,這些包根據某些約定組織在一起。在一個包目錄中,以_test.go結尾的文件不是go build命令編譯的目標,而是go test編譯的目標。

  在*_test.go文件中,三種函數需要特殊對待,即

  1. 功能測試函數
  2. 基准測試函數 :名稱以benchmark開頭,用來檢測某些操作的性能。
  3. 示例運行測試函數 :以Example來測試某些操作的性能。用來提供機器檢查過的文檔。

Test 函數

  每一個測試文件必須導入testing包,簽名如下

import "testing"

func TestName(t *testing.T)  {
	
}

 功能測試函數必須以Test開頭,可選的后綴名稱必須以大寫字母開頭:

import "testing"

func TestSin(t *testing.T)  {
	
}

func TestCos(t *testing.T)  {
	
}

示例 

  如下編譯一個測試連接mysql的單元測試,必須保證同目錄的情況下,否則找不到,創建一個mysql.go用來編寫連接的代碼

package mysql

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

func findByPk(pk int) int {
    var num int = 0
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/plugin?charset=utf8")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()
    stmtOut, err := db.Prepare("select id from t_admin where id=?")
    if err != nil {
        panic(err.Error())
    }
    defer stmtOut.Close()

    err = stmtOut.QueryRow(pk).Scan(&num)
    if err != nil {
        panic(err.Error())
    }
    return num
}

 在同目錄下創建一個mysql_test.go用來編寫測試代碼

package mysql

import (
    "testing"
)

func Test_findByPk(t *testing.T) {
    num := findByPk(1)
    t.Log(num)
}

 運行

  • go test: 在當前目錄下運行所有測試用例 
  • go test -file *_test.go : 運行指定測試文件。如 go test -file mysql_test.go
  • go test -V: 顯示詳細結果
  • go test -run="Test_XXX" : 指定運行某個方法

測試覆蓋率

  一個測試套件覆蓋待測試包的比例稱為測試的覆蓋率。覆蓋率無法直接通過數量來衡量。任何事情都是動態的。即使最微小的程序都無法精准的測量。語句覆蓋率是指部分語句在一次執行中至少執行一次。go語言中提供cover工具

# go test -cover
PASS
coverage: 100.0% of statements
ok    jdGoBackend/library/sdk/jd      0.017s

  go還提供了將輸出打印報告的方法。

# go test -cover -coverprofile=c.out

 然后可以通過html方法顯示,使用cover工具來處理生成的記錄信息,該命令會打開本地的瀏覽器窗口生成一個HTML報告。

# go tool cover -html=c.out  

基准測試 

  基准測試是在一定的工作負載之下檢測程序性能的一種方法,在Go里面,基准測試函數看上去像一個測試函數。但前綴是Benchmark並且擁有一個*testing.B參數用來提供大多數和*testing.T相同的方法。額外增加一些與性能檢測相關的方法。還提供了一個整數檢測成員N,用來檢測執行的次數。

  基本格式

func BenchmarkName(b *testing.B){
    // ...
}

  下面的基准測試在一個循環中調用了IsPaling共 N次

import "testing"

func BenchmarkIsPaling(b *testing.B)  {
	for i:=0; i< b.N; i++{
		IsPaling("aaaaa")
	}
}

 和測試不同的是,默認情況下不會執行任何基准測試,需要標記-bench的參數指定要運行的基准參數。

#  go test -bench=IsPaling
BenchmarkIsPaling-8        10000000               203 ns/op
PASS
ok      jdGoBackend/library/sdk/jd      2.255s

  就上面的運行結果,基礎名稱BenchmarkIsPaling-8后綴8表示GOMAXPROCS的值。這個數字對並發基准測試很重要。

  報告告訴我們每次IsPaling調用耗費0.23ms。這個是1 000 000 0次調用的平均值

我們可以通過-benchmen再報告中包含了內存分配統計數據。這里和優化之前的內存分配進行比較。

# go test -bench=. -benchmen  // . 通配符匹配所有
PASS 
BenchmarkIsPaling  1000000 1026 ns/op 304 B/op 4 allocs/op

 最后,性能比較函數知識普通的代碼,它們的表現形式通常是帶有一個參數的函數,被多個不同Benchmark函數傳入不同的值來調用。如下

func benchmark(b *testing.B, size int){/* ... */}
func Benchmark10(b *testing.B){ benchmark(b, 10) }
func Benchmark100(b *testing.B){ benchmark(b, 100) }
func Benchmark1000(b *testing.B){ benchmark(b, 1000) }

 參數size指定了輸入的大小,每個Benchmark函數傳入的值都不同但是在每個函數內部是一個常量,不要使用b.N作為輸入的大小。除非把它當作固定大小輸入的循環次數。否則該基准測試的結果毫無意義。

最后測試牽扯到性能,將會在性能模塊詳細介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM