基础
Go语言中的测试依赖go test命令。
go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。
在*_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数
类型 | 格式 | 作用 |
---|---|---|
基础测试 | 前缀为Test | 测试程序的逻辑是否正确 |
性能测试 | 前缀为Benchmark | 测试程序的性能 |
提供示例 | 前缀为Example | 为程序提供示例 |
go test中可用的参数有:
go test是go语言自带的测试工具,其中包含的是两类,单元测试和性能测试
通过go help test可以看到go test的使用说明:
格式形如: go test [-c] [-i] [build flags] [packages] [flags for test binary]
参数解读:
-c : 编译go test成为可执行的二进制文件,但是不运行测试。
-i : 安装测试包依赖的package,但是不运行测试。
关于build flags,调用go help build,这些是编译运行过程中需要使用到的参数,一般设置为空
关于packages,调用go help packages,这些是关于包的管理,一般设置为空
关于flags for test binary,调用go help testflag,这些是go test过程中经常使用到的参数
-test.v : 是否输出全部的单元测试用例(不管成功或者失败),默认没有加上,所以只输出失败的单元测试用例。
-test.run pattern: 只跑哪些单元测试用例
-test.bench patten: 只跑那些性能测试用例
-test.benchmem : 是否在性能测试的时候输出内存情况
-test.benchtime t : 性能测试运行的时间,默认是1s
-test.cpuprofile cpu.out : 是否输出cpu性能分析文件
-test.memprofile mem.out : 是否输出内存性能分析文件
-test.blockprofile block.out : 是否输出内部goroutine阻塞的性能分析文件
-test.memprofilerate n : 内存性能分析的时候有一个分配了多少的时候才打点记录的问题。这个参数就是设置打点的内存分配间隔,也就是profile中一个sample代表的内存大小。默认是设置为512 * 1024的。如果你将它设置为1,则每分配一个内存块就会在profile中有个打点,那么生成的profile的sample就会非常多。如果你设置为0,那就是不做打点了。
你可以通过设置memprofilerate=1和GOGC=off来关闭内存回收,并且对每个内存块的分配进行观察。
-test.blockprofilerate n: 基本同上,控制的是goroutine阻塞时候打点的纳秒数。默认不设置就相当于-test.blockprofilerate=1,每一纳秒都打点记录一下
-test.parallel n : 性能测试的程序并行cpu数,默认等于GOMAXPROCS。
-test.timeout t : 如果测试用例运行时间超过t,则抛出panic
-test.cpu 1,2,4 : go test -cpu=1,2,4 将会执行 3 次,其中 GOMAXPROCS 值分别为 1,2,和 4。
-test.short : 将那些运行时间较长的测试用例运行时间缩短
性能测试示例
性能测试函数需要以Benchmark开头,并且参数是*testing.B:
func Division(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为0")
}
return a / b, nil
}
// go test -bench '.*' .
func Benchmark_Division(b *testing.B) {
for i := 0; i < b.N; i++ { //use b.N for looping
Division(4, 5)
}
}
func Benchmark_TimeConsumingFunction(b *testing.B) {
b.StopTimer() //调用该函数停止压力测试的时间计数
//做一些初始化的工作,例如读取文件数据,数据库连接之类的,
//这样这些时间不影响我们测试函数本身的性能
b.StartTimer() //重新开始时间
for i := 0; i < b.N; i++ {
Division(4, 5)
}
}
使用go test -bench '.*' .
运行测试:
goos: windows
goarch: amd64
pkg: mytest/testperformancetest
cpu: AMD Ryzen 9 3900X 12-Core Processor
Benchmark_Division-24 1000000000 0.2467 ns/op
Benchmark_TimeConsumingFunction-24 1000000000 0.2433 ns/op
PASS
ok mytest/testperformancetest 0.615s
其中Benchmark_Division-24表示对Division函数进行基准测试,数字24表示GOMAXPROCS的值,这个对于并发基准测试很重要。1000000000和0.2467 ns/op表示每次调用Benchmark_Division函数耗时0.2467 ns,这个结果是1000000000次调用的平均值。
可用使用-test.cup n来设置cpu数量:
例如 go test -bench '.*' -cpu 4 .
goos: windows
goarch: amd64
pkg: mytest/testperformancetest
cpu: AMD Ryzen 9 3900X 12-Core Processor
Benchmark_Division-4 1000000000 0.2448 ns/op
Benchmark_TimeConsumingFunction-4 1000000000 0.2459 ns/op
PASS
ok mytest/testperformancetest 0.616s
增加-test.benchmem来显示内存占用:
例如 go test -bench '.*' -benchmem .
:
goos: windows
goarch: amd64
pkg: mytest/testperformancetest
cpu: AMD Ryzen 9 3900X 12-Core Processor
Benchmark_Division-24 1000000000 0.2452 ns/op 0 B/op 0 allocs/op
Benchmark_TimeConsumingFunction-24 1000000000 0.2461 ns/op 0 B/op 0 allocs/op
PASS
ok mytest/testperformancetest 0.623s
测试斐波那契递归和非递归的性能对比
// 斐波那契数列:后一个数等于前面两个数的和
func fb_recursion(num int) int {
if num <= 2 {
return 1
}
return fb_recursion(num - 1) + fb_recursion(num -2)
}
func fb_norecursion(num int) int {
l := make([]int, num+1)
for i := 1; i <= num; i++ {
if i <= 2 {
l[i] = 1
} else {
l[i] = l[i-1] + l[i-2]
}
}
return l[num]
}
// 性能测试
func Benchmark_fb_recursion(b *testing.B) {
for i := 40; i <= 43; i++ {
b.Run(strconv.Itoa(i), func(b *testing.B) {
fb_recursion(i)
})
}
}
func Benchmark_fb_norecursion(b *testing.B) {
for i := 40; i <= 43; i++ {
b.Run(strconv.Itoa(i), func(b *testing.B) {
fb_norecursion(i)
})
}
}
该测试分别对比了递归和非递归版本运行计算40-43的斐波那契数列的性能,可以看到,非递归版本的性能优势是很明显的:go test -bench '.*fb.*' -benchmem -benchtime 2s -cpu 1 .
goos: windows
goarch: amd64
pkg: mytest/testperformancetest
cpu: AMD Ryzen 9 3900X 12-Core Processor
Benchmark_fb_recursion/40 1000000000 0.3809 ns/op 0 B/op 0 allocs/op
Benchmark_fb_recursion/41 1000000000 0.5769 ns/op 0 B/op 0 allocs/op
Benchmark_fb_recursion/42 1000000000 1.047 ns/op 0 B/op 0 allocs/op
Benchmark_fb_recursion/43 1000000000 1.781 ns/op 0 B/op 0 allocs/op
Benchmark_fb_norecursion/40 1000000000 0 B/op 0 allocs/op
Benchmark_fb_norecursion/41 1000000000 0 B/op 0 allocs/op
Benchmark_fb_norecursion/42 1000000000 0 B/op 0 allocs/op
Benchmark_fb_norecursion/43 1000000000 0 B/op 0 allocs/op
PASS
ok mytest/testperformancetest 124.822s
性能测试中跳过某些步骤
func Benchmark_TimeConsumingFunction(b *testing.B) {
b.StopTimer() //调用该函数停止压力测试的时间计数
//做一些初始化的工作,例如读取文件数据,数据库连接之类的,
//这样这些时间不影响我们测试函数本身的性能
b.StartTimer() //重新开始时间
for i := 0; i < b.N; i++ {
Division(4, 5)
}
}
版权声明
本文章由作者“衡于墨”创作,转载请注明出处,未经允许禁止用于商业用途
评论区#
还没有评论哦,期待您的评论!
引用发言