1. 程式人生 > 實用技巧 >GO程式設計(打卡)-Task12: 單元測試

GO程式設計(打卡)-Task12: 單元測試

目錄

單元測試

go test 工具

包目錄內,所有以_test.go為字尾名的原始碼檔案都是go test測試的一部分,不會被go build編譯到最終的可執行檔案中

單元測試函式 函式名字首為Test 測試程式的一些邏輯行為是否正確
基準測試函式 函式名字首為Benchmark 測試函式的效能
示例函式 函式名字首為Example 為文件提供示例文件

測試框架 testing
測試命令 go test xxx
測試檔案 xxx_test.go

測試函式

  • 格式

    每個測試函式必須匯入testing包, 引數t用於報告測試失敗和附加的日誌資訊

    func TestName(t *testing.T){
        //...
    }
    
  • 示例

    // split/split.go
    
    package split
    import "strings"
    
    func Split(s, sep string) (result []string) {
        i := strings.Index(s, sep)
        for i > -1 {
            result = append(result, s[:i])
            s = s[i+1:]
            // s = s[i+len(sep):] // 這裡使用len(sep)獲取sep的長度
            i = strings.Index(s, sep)
        }
        result = append(result, s)
        return
    }
    
    // split/split_test.go
    
    package split
    
    import (
    	"reflect"
    	"testing"
    )
    
    func TestSplit(t *testing.T) { // 測試函式名必須以Test開頭,必須接收一個*testing.T型別引數
    	got := Split("a:b:c", ":")         // 程式輸出的結果
    	want := []string{"a", "b", "c"}    // 期望的結果
    	if !reflect.DeepEqual(want, got) { // 因為slice不能比較直接,藉助反射包中的方法比較
    		t.Errorf("excepted:%v, got:%v", want, got) // 測試失敗輸出錯誤提示
    	}
    }
    

    在split路徑下執行 go test

    新增測試用例

    func TestMoreSplit(t *testing.T) {
    	got := Split("abcd", "bc")
    	want := []string{"a", "d"}
    	if !reflect.DeepEqual(want, got) {
    		t.Errorf("excepted:%v, got:%v", want, got)
    	}
    }
    

    執行指定測試用例

    修改split函式中的程式碼 s = s[i+1:]s = s[i+len(sep):]

  • 測試組 新增更多測試用例

    func TestSplit(t *testing.T) {
    	// 定義一個測試用例型別
    	type test struct {
    		input string
    		sep   string
    		want  []string
    	}
    	// 定義一個儲存測試用例的切片
    	tests := []test{
    		{input: "a:b:c", sep: ":", want: []string{"a", "b", "c"}},
    		{input: "a:b:c", sep: ",", want: []string{"a:b:c"}},
    		{input: "abcd", sep: "bc", want: []string{"a", "d"}},
    		{input: "沙河有沙又有河", sep: "沙", want: []string{"河有", "又有河"}},
    	}
    	// 遍歷切片,逐一執行測試用例
    	for _, tc := range tests {
    		got := Split(tc.input, tc.sep)
    		if !reflect.DeepEqual(got, tc.want) {
    			t.Errorf("excepted:%#v, got:%#v", tc.want, got)
    		}
    	}
    }
    

  • 子測試 便於定位測試用例是否通過

    func TestSplit(t *testing.T) {
    	type test struct { // 定義test結構體
    		input string
    		sep   string
    		want  []string
    	}
    	tests := map[string]test{ // 測試用例使用map儲存
    		"simple":      {input: "a:b:c", sep: ":", want: []string{"a", "b", "c"}},
    		"wrong sep":   {input: "a:b:c", sep: ",", want: []string{"a:b:c"}},
    		"more sep":    {input: "abcd", sep: "bc", want: []string{"a", "d"}},
    		"leading sep": {input: "沙河有沙又有河", sep: "沙", want: []string{"河有", "又有河"}},
    	}
    	for name, tc := range tests {
    		t.Run(name, func(t *testing.T) { // 使用t.Run()執行子測試
    			got := Split(tc.input, tc.sep)
    			if !reflect.DeepEqual(got, tc.want) {
    				t.Errorf("excepted:%#v, got:%#v", tc.want, got)
    			}
    		})
    	}
    }
    

    執行指定子測試用例

  • 測試覆蓋率

    測試覆蓋率是你的程式碼被測試套件覆蓋的百分比 go test -cover檢視覆蓋率

基準函式

  • 格式

    基準測試必須要執行b.N

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

    func BenchmarkSplit(b *testing.B) {
      for i := 0; i < b.N; i++ {
      	Split("沙河有沙又有河", "沙")
      }
    }
    

    go test -bench=Split

    -benchmem引數,來獲得記憶體分配的統計資料

示例函式

  • 格式

    基準測試必須要執行b.N

    func ExampleName(b *testing.B){
        // ...
    }
    
  • 示例

    func ExampleSplit() {
      fmt.Println(Split("a:b:c", ":"))
      fmt.Println(Split("沙河有沙又有河", "沙"))
      // Output:
      // [a b c]
      // [ 河有 又有河]
    }
    

參考

https://github.com/datawhalechina/go-talent/blob/master/10.反射機制.md
https://www.cnblogs.com/nickchen121/p/11517443.html