Golang - 測試


下面展示的所有示例中代碼都在這裏


測試文件的命名

  • xxx_test.go
  • 比如測 split.go 文件, 命名就爲 split_test.go

單元測試

子測試集

package split // 導入本包

import (
	"reflect"
	"testing"
)

// 命名最好以 Test 開頭, 參數必須是這個
func TestSplit(t *testing.T) {
	get := Split("a:b:c", ":")                   // 我們做測試的請求
	want := []string{"a", "b", "c"}              // 期望得到的結果
	if ok := reflect.DeepEqual(get, want); !ok { // 這是一個深度對比, 可以對比引用類型
		t.Fatalf("期望結果: %v, 實際結果: %v\n", want, get)
	}
}

func TestNoneSplit(t *testing.T) {
	get := Split("a:b:c", "|")
	want := []string{"a:b:c"}
	if ok := reflect.DeepEqual(get, want); !ok {
		t.Fatalf("期望結果: %v, 實際結果: %v\n", want, get)
	}
}

// 分隔符是多個字符
func TestMulitiSplit(t *testing.T) {
	get := Split("abcabcabca", "bc")
	want := []string{"a", "a", "a", "a"}
	if ok := reflect.DeepEqual(get, want); !ok {
		t.Fatalf("期望結果: %v, 實際結果: %v\n", want, get)
	}
}


子測試組

package split

import (
	"reflect"
	"testing"
)

// 測試組
func TestGroupSplit(t *testing.T) {

	// 存放測試數據的結構體
	type test struct {
		str  string
		sep  string
		want []string
	}

	// 實例化各個測試用例
	tests := map[string]test{
		"normal": test{
			str:  "a:b:c",                 // 測試的字符串
			sep:  ":",                     // 測試的分隔符
			want: []string{"a", "b", "c"}, // 想要得到的結果
		},
		"none": test{
			str:  "a:b:c",
			sep:  "-",
			want: []string{"a:b:c"},
		},
		"multi": test{
			str:  "a::b::c",
			sep:  "::",
			want: []string{"a", "b", "c"},
		},
		"multi2": test{
			str:  "1231",
			sep:  "1",
			want: []string{"", "23", ""},
		},
	}

	// 測試
	for k, v := range tests {
		t.Run(k, func(t *testing.T) {
			r := Split(v.str, v.sep)
			if !reflect.DeepEqual(r, v.want) {
				t.Errorf("excepted: %#v, get: %#v", v.want, r)
			}
		})

		// v1.7 之前的寫法
		// r := Split(v.str, v.sep)
		// if ok := reflect.DeepEqual(v.want, r); !ok {
		// 	t.Fatalf("%v: 期望得到: %#v, 實際得到: %v", k, v.want, r)
		// }
	}
}


性能測試

簡單測試

package split

// 命名由 Benchmark 開頭, 參數固定
func BenchmarkSplit(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Split("a:b:c", ":")
	}
}


併發性能

package split

func BenchmarkSplitParallel(b *testing.B) {
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			Split("1231", "1")
		}
	})
}


性能對比

package fib

import "testing"

// 當傳入的數據量不同, 測試函數運算的耗時
// 這個 benchmark 是小寫, 用於調用
func benchmarkFib(b *testing.B, n int) {
	for i := 0; i < b.N; i++ {
		Fib(n)
	}
}

func BenchmarkFib2(b *testing.B) {
	benchmarkFib(b, 2)
}

func BenchmarkFib20(b *testing.B) {
	benchmarkFib(b, 20)
}
func BenchmarkFib100(b *testing.B) {
	benchmarkFib(b, 200)
}


設置 cpu 數量, 清除耗時

// 設置 cpu 數量
b.SetParallelism(1)

// 清除上邊代碼的耗時
b.ResetTimer()


測試前後加操作

全局前後增加操作 - TestMain

func TestMain(m *testing.M) {
	fmt.Println("start test...")
	r := m.Run()
	fmt.Println("end test...")
	os.Exit(r)
}


單一用例前後增加操作 - setup 和 teardown

package split


func TestMultiSplit(t *testing.T) {
	f := setupT(t)
	defer f(t)

	get := Split("a::b::c", "::")
	want := []string{"a", "b", "c"}

	if ok := reflect.DeepEqual(get, want); !ok {
		t.Fatalf("期望結果: %v, 實際結果: %v\n", want, get)
	} else {
		t.Log("pass test...")
	}
}

// 給單一測試用例加前後操作, 如果是性能測試, t 改 b 即可
func setupT(t *testing.T) func(t *testing.T) {
	t.Log("start test...")      // 測試前執行
	return func(t *testing.T) { // 返回函數名, 使用時 defer 調用
		t.Log("end test...")
	}
}


運行測試

單元測試

go test -v  # 輸出詳細信息

# 函數形式 子測試
go test -run=None -v  # 函數名字包含 None 的測試用例

# 組形式 子測試
go test -run Split/None -v  # 函數名/用例名, 如果有多個組, 需要聲明函數名, 否則可以省略

# 測試覆蓋率
go test -cover

# 將覆蓋率輸出到文件
go test -cover -coverprofile=c.out

# 將文件內容輸出到瀏覽器
go tool cover -html=c.out

# 如果遇到如下報錯說明目錄下沒有 go 的二進制文件, 添加即可
cover: cannot run go list: fork/exec /usr/local/go/bin/go: no such file or directory


基準測試

# -bench 指定測試的函數, . 表示所有
go test -bench=.

# 可以指定使用幾個cpu, 輸出內存信息等
go test -bench=Split -cpu=2 -benchmem

"""
結果展示:

goos: darwin
goarch: amd64
pkg: code.oldboy.com/split

# 使用了 4 個 cpu		 測試次數			   每次操作消耗時間		 每次操作佔用字節數   每次操作申請內存次數
BenchmarkSplit-4         3000000               420 ns/op             112 B/op          3 allocs/op
PASS
ok      code.oldboy.com/split   1.710s

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