*_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 获得内存分配的统计数据
  1. // split/split_test.go
  2. package split
  3. import (
  4. "reflect"
  5. "testing"
  6. )
  7. func TestSplit(t *testing.T) { // 测试函数名必须以Test开头,必须接收一个*testing.T类型参数
  8. got := Split("a:b:c", ":") // 程序输出的结果
  9. want := []string{"a", "b", "c"} // 期望的结果
  10. if !reflect.DeepEqual(want, got) { // 因为slice不能比较直接,借助反射包中的方法比较
  11. t.Errorf("excepted:%v, got:%v", want, got) // 测试失败输出错误提示
  12. }
  13. }

go test 运行

  1. // split/split_test.go
  2. package split
  3. import (
  4. "reflect"
  5. "testing"
  6. )
  7. func TestSplit(t *testing.T) { // 测试函数名必须以Test开头,必须接收一个*testing.T类型参数
  8. got := Split("a:b:c", ":") // 程序输出的结果
  9. want := []string{"a", "b", "c"} // 期望的结果
  10. if !reflect.DeepEqual(want, got) { // 因为slice不能比较直接,借助反射包中的方法比较
  11. t.Errorf("excepted:%v, got:%v", want, got) // 测试失败输出错误提示
  12. }
  13. }

基准测试 Benchmark

  1. func BenchmarkSplit(b *testing.B) {
  2. for i := 0; i < b.N; i++ {
  3. Split("枯藤老树昏鸦", "老")
  4. }
  5. }
  1. goos: darwin
  2. goarch: amd64
  3. pkg: github.com/youzeliang/gopractice/base/test/strings
  4. BenchmarkSplit-4 9668559 121 ns/op
  5. PASS
  6. ok github.com/youzeliang/gopractice/base/test/strings 1.300s

数字4表示GOMAXPROCS的值,这个对于并发基准测试很重要。9668559和121ns/op表示每次调用Split函数耗时121ns,这个结果是9668559次调用的平均值。

为基准测试添加-benchmem参数,来获得内存分配的统计数据。

go test -bench=Split -benchmem

  1. goos: darwin
  2. goarch: amd64
  3. pkg: github.com/youzeliang/gopractice/base/test/strings
  4. BenchmarkSplit-4 8687689 118 ns/op 48 B/op 2 allocs/op
  5. PASS
  6. ok github.com/youzeliang/gopractice/base/test/strings 1.173s

48 B/op表示每次操作内存分配了48字节

性能比较函数

编写了一个计算斐波那契数列的函数如下:

  1. // fib.go
  2. // Fib 是一个计算第n个斐波那契数的函数
  3. func Fib(n int) int {
  4. if n < 2 {
  5. return n
  6. }
  7. return Fib(n-1) + Fib(n-2)
  8. }

性能比较函数如下:

  1. // fib_test.go
  2. func benchmarkFib(b *testing.B, n int) {
  3. for i := 0; i < b.N; i++ {
  4. Fib(n)
  5. }
  6. }
  7. func BenchmarkFib1(b *testing.B) { benchmarkFib(b, 1) }
  8. func BenchmarkFib2(b *testing.B) { benchmarkFib(b, 2) }
  9. func BenchmarkFib3(b *testing.B) { benchmarkFib(b, 3) }
  10. func BenchmarkFib10(b *testing.B) { benchmarkFib(b, 10) }
  11. func BenchmarkFib20(b *testing.B) { benchmarkFib(b, 20) }
  12. 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标志一同使用。

  1. func BenchmarkSplitParallel(b *testing.B) {
  2. // b.SetParallelism(1) // 设置使用的CPU数
  3. b.RunParallel(func(pb *testing.PB) {
  4. for pb.Next() {
  5. Split("枯藤老树昏鸦", "老")
  6. }
  7. })
  8. }

还可以通过在测试命令后添加-cpu参数如go test -bench=. -cpu 1来指定使用的CPU数量。

示例函数示例

  1. func ExampleSplit() {
  2. fmt.Println(split.Split("a:b:c", ":"))
  3. fmt.Println(split.Split("aa1aa", "1"))
  4. // Output:
  5. // [a b c]
  6. // [aa aa]
  7. }

go test -run Example