go单元测试-基本概念及命令

2023/05/01 go

本文主要介绍golang的单元测试,包含单元测试基本概念,如何进行不同函数单元测试,压测等

一、单元测试

单元测试框架基准

1、文件命名规则: 含有单元测试代码的go文件必须以_test.go结尾,一般测试文件和待测试文件在同一个文件夹内。

2、函数声明规则: 测试函数的签名必须接收一个指向testing.T类型的指针,并且函数没有返回值。

3、函数命名规则:单元测试的函数名必须以Test开头,是可导出公开的函数,最好是Test+要测试的方法函数名。

常用选项

-v  :查看更详细的测试结果输出
-run:指定输出哪个函数的测试结果,默认为当前路径下所有单元测试函数
-coverprofile:指定生成覆盖率测试输出文件

二 、基准测试

工具推荐:benchstat

安装:go get golang.org/x/perf/cmd/benchstat (将会安装到$GOPATH/bin目录下,需要将该目录配置到环境变量中)

1、什么是基准测试

基准测试是一种测试代码性能的方法,主要通过测试CPU和内存等因素,来评估代码性能,以此来调优代码性能。

2、编写规则

1、文件命名规则:  
   含有单元测试代码的go文件必须以_test.go结尾,Go语言测试工具只认符合这个规则的文件  
   单元测试文件名_test.go前面的部分最好是被测试的方法所在go文件的文件名。
 
2、函数声明规则:  
   测试函数的签名必须接收一个指向testing.B类型的指针,并且函数没有返回值。
 
3、函数命名规则:  
   单元测试的函数名必须以Benchmark开头,是可导出公开的函数,最好是Benchmark+要测试的方法函数名。
 
4、函数体设计规则:
   b.N 是基准测试框架提供,用于控制循环次数,循环调用测试代码评估性能。
    b.ResetTimer()/b.StartTimer()/b.StopTimer()用于控制计时器,准确控制用于性能测试代码的耗时。

3、基准测试命令选项

命令参数

  • -bench=. 表示指定执行测试函数。.表示执行所有,如果修改为go test -bench=BenchmarkJoinStrUseSprint那么只会执行BenchmarkJoinStrUseSprint
  • -benchtime=1s指定执行时间为1s
  • -benchmem显示内存情况
  • -count=1表示执行一次

响应参数

  • goos: linux 操作系统
  • goarch: amd64 系统体系架构
  • BenchmarkJoinStrUseNor-8 执行的函数名称以及对应的GOMAXPROCS值。
  • 79888155b.N的值
  • 15.5 ns/op 执行一次函数所花费的时间
  • 0 B/op 执行一次函数分配的内存
  • 0 allocs/op 执行一次函数所分配的内存次数

这几个数当然是越小越好

操作命令go test  -bench=. benchtime=3s -run=none -cpuprofile
    -bench      go test默认不会基准测试需要使用bench启动基准测试指定匹配基准测试的函数,“.表示运行所有基准测试
    
    -benchtime  测试时间默认为1s如果想测试运行时间更长-benchtime指定
    
    -run        默认情况下go test会运行单元测试为防止其干扰基准测试输出结果
                    可使用-run过滤单元测试使用none完全屏蔽,“.运行所有单元测试  
                    
    -count      指定执行多少次
 
        $ go test  -bench=. -run=none
        goos: linux
        goarch: amd64
        pkg:   learning/test/benchmark
        BenchmarkSprintf-12       20000000                70.6   ns/op
        PASS
        ok      learning/test/benchmark   1.485s
 
        其中BenchmarkSprintf-1212表示GOMAXPROCS20000000表示循环次数70.6 ns/op表示单次循环操作花费时间
 
    -cpuprofile 生成运行时CPU详细信息
    go test  -bench=. -run=none -cpuprofile=xxx xxx

    (pprof) quit
 
    -benchmem     提供每次操作分配内存的次数以及每次分配的字节数
    $ go test  -bench=. -benchmem -run=none
    goos: linux
    goarch: amd64
    pkg:   learning/test/benchmark
    BenchmarkSprintf-12       20000000                70.5   ns/op            16   B/op          2 allocs/op
    BenchmarkFormat-12      20000000                77.1   ns/op            18   B/op          2 allocs/op
    BenchmarkItoa-12        20000000                78.4   ns/op            18   B/op          2 allocs/op
    PASS
    ok      learning/test/benchmark   4.754s

4、实例1:slice的不同使用方式

package benchmark
 
import (
    "testing"
)
 
const TotalTimes = 1000000
 
func StaticCapacity() {
 
    // 提前一次性分配好slice所需内存空间,中间不需要再扩容,len为0,cap为1000000
    var s   = make([]byte, 0, TotalTimes)
 
    for i :=   0; i < TotalTimes; i++ {
        s = append(s, 0)
        //fmt.Printf("len   = %d, cap = %d\n", len(s), cap(s))
    }
}
 
func DynamicCapacity() {
 
    // 依赖slice底层自动扩容,中间会有很多次扩容,每次都从新分配一段新的内存空间,
    // 然后把数据拷贝到新的slice内存空间,然后释放旧空间,导致引发不必要的GC
    var s []byte
 
    for i :=   0; i < TotalTimes; i++ {
        s = append(s, 0)
        //fmt.Printf("len   = %d, cap = %d\n", len(s), cap(s))
    }
}
 
func BenchmarkStaticCapacity(b   *testing.B) {
    b.ResetTimer()
 
    for i :=   0; i < b.N; i++ {
        StaticCapacity()
    }
}
 
func BenchmarkDynamicCapacity(b   *testing.B) {
    b.ResetTimer()
 
    for i :=   0; i < b.N; i++ {
        DynamicCapacity()
    }
}

测试结果:

模式 操作时间消耗 ns/op 内存分配大小 B/op 内存分配次数 allocs/op
$ go test  -bench=. -run=none -benchmem 
goos: linux
goarch: amd64
pkg: learning/testing/benchmark
BenchmarkStaticCapacity-12          3000            912668   ns/op         1007617   B/op          1 allocs/op
BenchmarkDynamicCapacity-12           1000           1935269   ns/op         5863427   B/op         35 allocs/op
PASS
ok      learning/testing/benchmark      4.914s

可以看出StaticCapacity 性能明显优于DynamicCapacity,所以如果同一个slice被大量循环使用,可提前一次性分配好适量的内存空间

总结:在代码设计过程中,对于性能要求比较高的地方,编写基准测试非常重要,这有助于我们开发出性能更优的代码。不过性能、可用性、复用性等也要有一个相对的取舍,不能为了追求性能而过度优化。

参考资料

  • https://github.com/sxs2473/go-performane-tuning
  • https://www.cnblogs.com/wayne666/p/10559900.html

Search

    公众号:豆仔gogo

    豆仔gogo

    Post Directory