单元测试编写原则
测试文件必须以 _test.go 结尾。
测试文件包名要和被测试函数所在包一致。
必须引入 testing 包。
单元测试函数必须以 Test 开头。
使用案例函数必须以 Example 开头,且有预期 Output 注释。
基准测试函数必须以 Benchmark 开头。
常用测试命令
# 单元测试 go test \ -v \ # 显示详细信息 -run=. # 只运行名称符合正则的单元测试和案例 -count=2 \ # 指定每个测试运行次数 mailer_test.go # 基准测试 go test \ -v \ # 显示详细信息 -bench=. \ # 指定待测试的 Benchmark 的正则匹配 -benchtime=5s \ # 指定每个基准测试运行时间,默认 1s,不指定则为自动设置 -count=2 \ # 指定每个测试运行次数 -benchmem \ # 显示内存分配 benchmark_test.go # 编译测试文件 go test \ -c \ # 编译测试文件 -o /tmp/gotest # 指定编译输出的文件位置,不指定则为 ./<package-name>.test # 使用编译的测试文件进行单元测试 ./<package-name>.test \ -test.v \ # 输出详细信息 -test.run . \ # 只运行名称符合正则的单元测试和案例 -test.count 2 # 指定每个测试运行次数,默认 1 # 使用编译的测试文件进行基准测试 ./<package-name>.test \ -test.v \ # 输出详细信息 -test.bench . \ # 只运行名称符合正则的基准测试 -test.benchmem \ # 基准测试时输出内存分配 -test.benchtime 5s \ # 指定每个基准测试运行时间,默认 1s,不指定则为自动设置 -test.count 2 # 指定每个测试运行次数,默认 1 # 覆盖率测试与分析 go test -coverprofile=c.out go tool cover -html=c.out # 基准测试与分析 sudo apt install graphviz -y go test -bench . -cpuprofile cpu.out go tool pprof cpu.out (pprof) web
案例
add.go
package main import "fmt" type Number struct { a int b int } func (num *Number) Multi() int { return num.a * num.b } func main() { num := Number{ a: 2, b: 6, } fmt.Println(num.Multi()) }
add_test.go
package main import ( "fmt" "testing" ) // 单元测试 func TestNumber_Multi(t *testing.T) { type fields struct { a int b int } tests := []struct { name string fields fields want int }{ {name: "1", fields: fields{a: 0, b: 2}, want: 0}, {name: "2", fields: fields{a: 3, b: -2}, want: -6}, {name: "3", fields: fields{a: 5, b: 9}, want: 45}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { num := &Number{ a: tt.fields.a, b: tt.fields.b, } if got := num.Multi(); got != tt.want { t.Errorf("Multi() = %v, want %v", got, tt.want) } }) } } // 使用案例 func ExampleNumber_Multi() { num := Number{ a: 2, b: 6, } fmt.Println(num.Multi()) // Output: // 12 } // 基准测试 func BenchmarkNumber_Multi(b *testing.B) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { num := &Number{ a: 33, b: 20, } if num.Multi() != 660 { b.FailNow() } } }
int 与 string 类型互相转换的基准测试
// strconv_test.go package test import ( "fmt" "strconv" "testing" ) func BenchmarkSprintf(b *testing.B) { n := 10 b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { fmt.Sprintf("%d", n) } } func BenchmarkItoa(b *testing.B) { n := 10 b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { strconv.Itoa(n) } } func BenchmarkFormatInt(b *testing.B) { n := int64(10) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { strconv.FormatInt(n, 10) } } func BenchmarkAtoi(b *testing.B) { n := "10" b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { strconv.Atoi(n) } } func BenchmarkParseInt(b *testing.B) { n := "10" b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { strconv.ParseInt(n, 10, 0) } b.StopTimer() }
测试结果
# -4 表示使用 4 核心 | CPU 基准测试次数 | 单次测试使用时间 | 单次测试处理的字节数 | 总的分配内存的次数 $ go test -bench=. goos: linux goarch: amd64 pkg: learn/test BenchmarkSprintf-4 6880945 172 ns/op 16 B/op 2 allocs/op BenchmarkItoa-4 183639746 6.36 ns/op 0 B/op 0 allocs/op BenchmarkFormatInt-4 180059316 6.38 ns/op 0 B/op 0 allocs/op BenchmarkAtoi-4 100000000 10.3 ns/op 0 B/op 0 allocs/op BenchmarkParseInt-4 36338739 29.6 ns/op 0 B/op 0 allocs/op PASS ok learn/test 8.164s
+= 与 buffer 字符串连接基准测试
// 利用 += 连接 func BenchmarkConcatStringByAdd(b *testing.B) { chars := []string{"1", "2", "3", "4", "5"} b.ResetTimer() for i := 0; i < b.N; i++ { ret := "" for _, char := range chars { ret += char } } b.StopTimer() } // 利用 buffer 连接 func BenchmarkConcatStringBytesBuffer(b *testing.B) { chars := []string{"1", "2", "3", "4", "5"} b.ResetTimer() for i := 0; i < b.N; i++ { var buf bytes.Buffer for _, char := range chars { buf.WriteString(char) } } b.StopTimer() }
测试结果
# 使用 -benchmem 参数等同于在测试程序中调用 b.ReportAllocs() $ go test -bench=. -benchmem goos: linux goarch: amd64 pkg: test BenchmarkConcatStringByAdd-4 4398699 278 ns/op 16 B/op 4 allocs/op BenchmarkConcatStringBytesBuffer-4 7655295 154 ns/op 64 B/op 1 allocs/op PASS ok test 2.844s
本文标题:golang 单元测试
版权声明:本文使用「署名-非商业性使用-相同方式共享」创作共享协议,转载或使用请遵守署名协议。
相关文章
上一篇:golang iota 笔记