在*_test.go
文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。
类型 | 格式 | 作用 |
---|---|---|
测试函数 | 函数名前缀为Test | 测试程序的一些逻辑行为是否正确 |
基准函数 | 函数名前缀为Benchmark | 测试函数的性能 |
示例函数 | 函数名前缀为Example | 为文档提供示例文档 |
命令 | 作用 | 示例 |
---|---|---|
go test | 运行测试文件 | |
go test - v | 测试函数名称和运行时间: | |
go test -v -run=”More” | go test命令后添加-run参数,它对应一个正则表达式,只有函数名匹配上的测试函数才会被go test命令执行。 | |
go test -cover | 测试覆盖率是你的代码被测试套件覆盖的百分比。通常我们使用的都是语句的覆盖率,也就是在测试中至少被运行一次的代码占总代码的比例。 | |
go test -cover -coverprofile=c.out | Go还提供了一个额外的-coverprofile参数,用来将覆盖率相关的记录信息输出到一个文件 | -coverprofile |
go tool cover -html=c.out | 使用cover工具来处理生成的记录信息,该命令会打开本地的浏览器窗口生成一个HTML报告。 | |
go test -bench=Split | 对Split函数进行基准测试 | |
go test -bench=Split -benchmem | 获得内存分配的统计数据 |
// 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) // 测试失败输出错误提示
}
}
go test 运行
// 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) // 测试失败输出错误提示
}
}
基准测试 Benchmark
func BenchmarkSplit(b *testing.B) {
for i := 0; i < b.N; i++ {
Split("枯藤老树昏鸦", "老")
}
}
goos: darwin
goarch: amd64
pkg: github.com/youzeliang/gopractice/base/test/strings
BenchmarkSplit-4 9668559 121 ns/op
PASS
ok github.com/youzeliang/gopractice/base/test/strings 1.300s
数字4表示GOMAXPROCS的值,这个对于并发基准测试很重要。9668559和121ns/op表示每次调用Split函数耗时121ns,这个结果是9668559次调用的平均值。
为基准测试添加-benchmem参数,来获得内存分配的统计数据。
go test -bench=Split -benchmem
goos: darwin
goarch: amd64
pkg: github.com/youzeliang/gopractice/base/test/strings
BenchmarkSplit-4 8687689 118 ns/op 48 B/op 2 allocs/op
PASS
ok github.com/youzeliang/gopractice/base/test/strings 1.173s
48 B/op表示每次操作内存分配了48字节
性能比较函数
编写了一个计算斐波那契数列的函数如下:
// fib.go
// Fib 是一个计算第n个斐波那契数的函数
func Fib(n int) int {
if n < 2 {
return n
}
return Fib(n-1) + Fib(n-2)
}
性能比较函数如下:
// fib_test.go
func benchmarkFib(b *testing.B, n int) {
for i := 0; i < b.N; i++ {
Fib(n)
}
}
func BenchmarkFib1(b *testing.B) { benchmarkFib(b, 1) }
func BenchmarkFib2(b *testing.B) { benchmarkFib(b, 2) }
func BenchmarkFib3(b *testing.B) { benchmarkFib(b, 3) }
func BenchmarkFib10(b *testing.B) { benchmarkFib(b, 10) }
func BenchmarkFib20(b *testing.B) { benchmarkFib(b, 20) }
func BenchmarkFib40(b *testing.B) { benchmarkFib(b, 40) }
运行基准测试:go test -bench=.
每个基准测试至少运行1秒。如果在Benchmark函数返回时没有到1秒,则b.N的值会按1,2,5,10,20,50,…增加,并且函数再次运行。
go test -bench=Fib40 -benchtime=20s
并行测试
func (b _B) RunParallel(body func(_PB))会以并行的方式执行给定的基准测试。
RunParallel会创建出多个goroutine,并将b.N分配给这些goroutine执行, 其中goroutine数量的默认值为GOMAXPROCS。用户如果想要增加非CPU受限(non-CPU-bound)基准测试的并行性, 那么可以在RunParallel之前调用SetParallelism 。RunParallel通常会与-cpu标志一同使用。
func BenchmarkSplitParallel(b *testing.B) {
// b.SetParallelism(1) // 设置使用的CPU数
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Split("枯藤老树昏鸦", "老")
}
})
}
还可以通过在测试命令后添加-cpu参数如go test -bench=. -cpu 1来指定使用的CPU数量。
示例函数示例
func ExampleSplit() {
fmt.Println(split.Split("a:b:c", ":"))
fmt.Println(split.Split("aa1aa", "1"))
// Output:
// [a b c]
// [aa aa]
}
go test -run Example