1. 基础测试 1.1 最简单的测试 1 2 3 4 5 6 7 func Add (x, y int ) int { return x + y } func Sub (x, y int ) int { return x - y }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func TestAdd (t *testing.T) { result := Add(3 , 5 ) if result != 8 { t.Fatalf("expected: %d, actual: %d" , 8 , result) } t.Log("test Add success." ) } func TestSub (t *testing.T) { result := Sub(3 , 5 ) if result != -2 { t.Fatalf("expected: %d, actual: %d" , -2 , result) } t.Log("test Sub success." ) }
1 2 3 4 5 6 7 go test go test -v go test -v -run TestAdd go help testflag
1.2 表格驱动测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func TestAddBatch (t *testing.T) { tests := []struct { a, b, c int }{ {1 , 2 , 3 }, {0 , 2 , 2 }, {-1 , 1 , 1 }, } for _, test := range tests { if actual := Add(test.a, test.b); actual != test.c { t.Errorf("Add(%d, %d); got %d; expected %d\n" , test.a, test.b, actual, test.c) } } }
1.3 覆盖率测试 1 2 3 go test -coverprofile=c.out go tool cover -html=c.out
1.4 Example Code测试 1 2 3 4 5 6 7 func Fib (n int ) int { if n <= 2 { return 1 } return Fib(n-1 ) + Fib(n-2 ) }
1 2 3 4 5 func ExampleFib () { fmt.Println(fib(10 )) }
2. 基准测试 基准测可以测试一段程序的运行性能及耗费CPU的程度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 func fib (n int ) int { a, b := 1 , 1 for i := 1 ; i <= n; i++ { if i == n { break } a, b = b, a+b } return a } func fibonacci () func () int { a, b := 1 , 1 return func () int { x := a a, b = b, a+b return x } } func fib2 (n int ) int { x := 0 f := fibonacci() for i := 1 ; i <= n; i++ { x = f() } return x } func fib3 (n int ) int { if n <= 2 { return 1 } return fib3(n-1 ) + fib3(n-2 ) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 func BenchmarkFib (b *testing.B) { n := 9 expected := 34 for i := 0 ; i < b.N; i++ { actual := fib(n) if actual != expected { b.Errorf("fib(%d), got %d, expected %d" , n, actual, expected) } } } func BenchmarkFib2 (b *testing.B) { n := 9 expected := 34 for i := 0 ; i < b.N; i++ { actual := fib2(n) if actual != expected { b.Errorf("fib2(%d), got %d, expected %d" , n, actual, expected) } } } func BenchmarkFib3 (b *testing.B) { n := 9 expected := 34 for i := 0 ; i < b.N; i++ { actual := fib3(n) if actual != expected { b.Errorf("fib3(%d), got %d, expected %d" , n, actual, expected) } } }
1 2 3 4 5 6 7 8 9 10 11 12 go test -bench=. BenchmarkFib-4 222371659 5.33 ns/op BenchmarkFib2-4 13082476 85.9 ns/op BenchmarkFib3-4 10054833 120 ns/op go test -bench=. -benchmem -benchtime=10s BenchmarkFib-4 1000000000 5.35 ns/op 0 B/op 0 allocs/op BenchmarkFib2-4 138880591 87.1 ns/op 48 B/op 3 allocs/op BenchmarkFib3-4 97723549 120 ns/op 0 B/op 0 allocs/op
ns/op
表示每一个操作消耗多少时间,单位是 纳秒ns
B/op
表示每一次操作需要分配的字节数
allocs/op
表示每次执行分配了多少次
1 2 3 4 go test -bench . -cpuprofile=cpu.out go tool pprof cpu.out (pprof) web
2.1 性能对比,int转string 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func BenchmarkSprintf (b *testing.B) { num := 10 b.ResetTimer() for i := 0 ; i < b.N; i++ { fmt.Sprintf("%d" , num) } } func BenchmarkFormat (b *testing.B) { num := int64 (10 ) b.ResetTimer() for i := 0 ; i < b.N; i++ { strconv.FormatInt(num, 10 ) } } func BenchmarkItoa (b *testing.B) { num := 10 b.ResetTimer() for i := 0 ; i < b.N; i++ { strconv.Itoa(num) } }
1 2 3 4 5 6 7 8 9 go test -bench=. -benchmem goos: darwin goarch: amd64 pkg: gomod/aaa BenchmarkSprintf-4 12190561 94.1 ns/op 16 B/op 2 allocs/op BenchmarkFormat-4 275836423 4.24 ns/op 0 B/op 0 allocs/op BenchmarkItoa-4 253071742 4.73 ns/op 0 B/op 0 allocs/op PASS ok gomod/aaa 5.386s
2.2 pprof 性能监控 1 2 3 4 5 6 7 8 9 10 11 12 13 func Fib (n int ) int { if n < 2 { return n } return Fib(n-1 ) + Fib(n-2 ) } func BenchmarkFib (b *testing.B) { for i := 0 ; i < b.N; i++ { Fib(10 ) } }
1 2 3 4 5 6 7 go test -bench=. -benchmem -cpuprofile cpu.out -memprofile mem.out go tool pprof cpu.out (pprof) top (pprof) list Fib go tool pprof -http=":8081" cpu.out
3. gomock 当待测试的函数/对象的依赖关系很复杂,并且有些依赖不能直接创建,例如数据库连接、文件I/O等。这种场景就非常适合使用 mock/stub 测试。简单来说,就是用 mock 对象模拟依赖项的行为。
3.1 安装 1 2 go get -u github.com/golang/mock/gomock go get -u github.com/golang/mock/mockgen
3.2 示例 3.2.1 待mock的代码 1 2 3 4 5 6 7 8 9 10 11 type DB interface { Get(key string ) (int , error) } func GetFromDB (db DB, key string ) int { if value, err := db.Get(key); err == nil { return value } return -1 }
3.2.2 生成mock代码 1 mockgen -source =db.go -destination=db_mock.go -package=main
3.2.3 测试代码 1 2 3 4 5 6 7 8 9 10 11 func TestGetFromDB (t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() m := NewMockDB(ctrl) m.EXPECT().Get(gomock.Eq("Tom" )).Return(0 , errors.New("not exist" )) if v := GetFromDB(m, "Tom" ); v != -1 { t.Fatalf("expected -1, but go %v" , v) } }
3.2.4 执行测试 1 2 3 4 5 6 $ go test . -cover -v === RUN TestGetFromDB --- PASS: TestGetFromDB (0.00s) PASS coverage: 92.9% of statements ok gomod/mock 1.030s coverage: 92.9% of statements
3.3 打桩 (stubs) 3.3.1 参数 (Eq, Any, Not, Nil) 1 2 3 4 m.EXPECT().Get(gomock.Eq("Tom" )).Return(0 , errors.New("not exist" )) m.EXPECT().Get(gomock.Any()).Return(630 , nil ) m.EXPECT().Get(gomock.Not("Sam" )).Return(0 , nil ) m.EXPECT().Get(gomock.Nil()).Return(0 , errors.New("nil" ))
3.3.2 返回值 (Return, Do, DoAndReturn) 1 2 3 4 5 6 7 8 9 10 m.EXPECT().Get(gomock.Not("Sam" )).Return(0 , nil ) m.EXPECT().Get(gomock.Any()).Do(func (key string ) { t.Log(key) }) m.EXPECT().Get(gomock.Any()).DoAndReturn(func (key string ) (int , error) { if key == "Sam" { return 630 , nil } return 0 , errors.New("not exist" ) })
3.3.3 调用次数 (Times, MaxTimes, MinTimes, AnyTimes) 1 2 3 4 5 6 7 8 9 func TestGetFromDB (t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() m := NewMockDB(ctrl) m.EXPECT().Get(gomock.Not("Sam" )).Return(0 , nil ).Times(2 ) GetFromDB(m, "ABC" ) GetFromDB(m, "DEF" ) }
调用顺序 (InOrder) 1 2 3 4 5 6 7 8 9 10 11 func TestGetFromDB (t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() m := NewMockDB(ctrl) o1 := m.EXPECT().Get(gomock.Eq("Tom" )).Return(0 , errors.New("not exist" )) o2 := m.EXPECT().Get(gomock.Eq("Sam" )).Return(630 , nil ) gomock.InOrder(o1, o2) GetFromDB(m, "Tom" ) GetFromDB(m, "Sam" ) }