一、测试基础概述
Go语言内建一套基于表格驱动测试的机制,只需编写少量代码,就可对特定功能进行单元测试和基准测试(性能测试或压力测试)
二、单元测试
所谓单元测试,是程序中对最小功能单元进行检查和验证。
Go进行单元测试非常简单:
创建一个go文件,在命名文件时以_test.go结尾,例如: unit_test.go
每个测试文件可支持多个测试用例的编写,每个测试用例函数需要以Test为前缀,且其只有一个*testing.T指针类型的入参。例如:
func TestAXXX( t *testing.T ){}
func TestBXXX( t *testing.T ){}
- 运行go test -v {testing_filename.go} 执行测试并打印测试详细数据
go test 指定文件时默认执行文件内的所有测试用例。可以使用-run参数选择需要的测试用例单独执行
以下对一个斐波那契数列计算函数的测试示例
测试目标功能函数:
//递归实现的斐波那契数列
func Fibonacci(n int) int {
rs := 0
//归
if n == 1 || n == 2 {
return 1
}
//递
rs = Fibonacci(n-1) + Fibonacci(n-2)
return rs
}
单元测试用例:
import "testing"
func TestFibonacci(t *testing.T) {
//先编一个预定义的参数和结果集
caseMap := make(map[int]int)
caseMap[1] = 1
caseMap[3] = 2
caseMap[5] = 5
caseMap[10] = 55
//测试运行一个函数,得到结果
for i, v := range caseMap {
rs := Fibonacci(i) //目标测试函数
if rs != v {
t.Fatalf("测试用例发现错误:参数%d,预计得到结果%d,实际得到结果%d", i, v, rs)
}
}
t.Log("测试结束,没有发现问题。")
}
//OUTPUT SUCCESSFUL:
=== RUN TestFibonacci
--- PASS: TestFibonacci (0.00s)
TestUnit_test.go:48: 测试结束,没有发现问题。
PASS
ok command-line-arguments 0.005s
//OUTPUT FAIL:
//为模拟测试失败,更改一个预定义结果集让其不通过
=== RUN TestFibonacci
--- FAIL: TestFibonacci (0.00s)
TestUnit_test.go:44: 测试用例发现错误:参数10,预计得到结果50,实际得到结果55
FAIL
FAIL command-line-arguments 0.005s
//注意:在使用go test 测试单个文件的时候,本人运行出现报错:
# command-line-arguments [command-line-arguments.test]
Code/go/src/mydemo/base/TestUnit_test.go:42:9: undefined: Fibonacci
FAIL command-line-arguments [build failed]
//可看到对测试目标运行测试时没有发现目标函数,看到后面[build failed]失败了,这里需要把测试目标的文件也添加到命令:
go test -v {testing_filename.go} {target_filename.go}
以上为最简单的功能测试实现,可以发现主要为testing包的支持,那么testing.T这个类型可以提供哪些测试功能呢?下面解析一下:
//标记失败但继续运行该测试
t.Fail()
//标记失败并立刻终止该测试
t.FailNow()
//打印测试日志
t.Log("日志记录...")
t.Logf("[Error]:%s/n","错误原因")
//t.Log() + t.Fail()
t.Error("劳资是日志")
//t.Logf() + t.Fail()
t.Errorf("[Error]:%s/n","错误原因")
//t.Log() + t.FailNow()
t.Fatal("日志记录...")
//t.Logf() + t.FailNow()
t.Fatalf("[Error]:%s/n","错误原因")
以上可看到主要是测试运行时的日志记录和错误处理功能
三、基准测试
基准测试可以测试一段程序的运行性能及耗费 CPU 的程度。Go 语言中提供了基准测试框架,使用方法类似于单元测试,使用者无须准备高精度的计时器和各种分析工具,基准测试本身即可以打印出非常标准的测试报告。
Go进行基准测试和单元测试一样简单:
创建一个go文件,在命名文件时以_test.go结尾,例如: benchmark_test.go
每个测试文件可支持多个测试用例的编写,每个测试用例函数需要以Benchmark为前缀,且其只有一个*testing.B指针类型的入参。例如:
func Benchmark_AXXX( t *testing.B ){}
func Benchmark_BXXX( t *testing.B ){}
- 运行go test -v -bench=. benchmark_test.go 执行基准测试
-bench=.表示运行 benchmark_test.go 文件里的所有基准测试,和单元测试中的-run类似
基准测试用例:
func BenchmarkFibonacci(b *testing.B) {
b.ReportAllocs() //内存开销
//b.N为常规写法
for i := 0; i < b.N; i++ {
Fibonacci(10)
}
}
//OUTPUT:
goos: darwin
goarch: amd64
BenchmarkFibonacci-4 10000000 204 ns/op 0 B/op 0 allocs/op
--- BENCH: BenchmarkFibonacci-4
PASS
ok command-line-arguments 2.257s
//10000000 表示测试的次数,也就是 testing.B 结构中提供给程序使用的 N。“204 ns/op”表示每一个操作耗费多少时间(纳秒)。
基准测试原理:
基准测试框架对一个测试用例的默认测试时间是 1 秒。开始测试时,当以 Benchmark 开头的基准测试用例函数返回时还不到 1 秒,那么 testing.B 中的 N 值将按 1、2、5、10、20、50……递增,同时以递增后的值重新调用基准测试用例函数。
通过-benchtime参数可以自定义测试时间:
go test -v -bench=. -benchtime=5s benchmark_test.go
//OUTPUT:
goos: darwin
goarch: amd64
BenchmarkFibonacci-4 30000000 204 ns/op 0 B/op 0 allocs/op
--- BENCH: BenchmarkFibonacci-4
PASS
ok command-line-arguments 6.337s
通过-benchmem参数以显示内存分配情况
go test -v -bench=BenchmarkFibonacci -benchmem benchmark_test.go
//OUTPUT:
goos: darwin
goarch: amd64
BenchmarkFibonacci-4 10000000 203 ns/op 0 B/op 0 allocs/op
--- BENCH: BenchmarkFibonacci-4
PASS
ok command-line-arguments 2.251s
//输出差不多,这个斐波那契例子内存分配几乎忽略不计
控制计时器
有些测试需要一定的启动和初始化时间,如果从 Benchmark() 函数开始计时会很大程度上影响测试结果的精准性。testing.B 提供了一系列的方法可以方便地控制计时器,从而让计时器只在需要的区间进行测试。
示例:
func Benchmark_Add_TimerControl(b *testing.B) {
// 重置计时器
b.ResetTimer()
// 停止计时器
b.StopTimer()
// 开始计时器
b.StartTimer()
var n int
for i := 0; i < b.N; i++ {
n++
}
}
四、性能分析
go提供两种pprof包来做代码的性能分析
- runtime/pprof : 基本性能分析包
- net/http/pprof : 基于runtime/pprof封装,并在http端口暴露
1. pprof是什么?
pprof是Go提供的可视化性能分析工具,在性能测试中读取分析样本的集合,并生成报告以可视化并帮助分析数据。pprof既能生成报告文件,也可以借助graphviz生成web界面。
你能看到什么?
- CPU Profiling:CPU 分析,按照一定的频率采集所监听的应用程序 CPU(含寄存器)的使用情况,可确定应用程序在主动消耗 CPU 周期时花费时间的位置
- Memory Profiling:内存分析,在应用程序进行堆分配时记录堆栈跟踪,用于监视当前和历史内存使用情况,以及检查内存泄漏
- Block Profiling:阻塞分析,记录 goroutine 阻塞等待同步(包括定时器通道)的位置
- Mutex Profiling:互斥锁分析,报告互斥锁的竞争情况
2.pprof使用方式
2.1 基于基准测试生成性能分析文件:
go test -bench=. -benchtime="3s" -cpuprofile=profile_cpu.out
运行完成后,会发现在当前目录生成两个文件
profile_cpu.out —— 分析报告文件
base.test —— 可执行程序
2.2 查看性能分析报告
- 终端查看
//使用go提供的工具在终端查看
go tool pprof base.test profile_cpu.out
//进入终端pprof查看模式
File: base.test
Type: cpu
Time: Jul 4, 2019 at 11:21am (CST)
Duration: 6.55s, Total samples = 6.02s (91.95%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
//输入help查看命令集合,可见还是功能还是很强大的。
(pprof) help
Commands:
callgrind Outputs a graph in callgrind format
comments Output all profile comments
disasm Output assembly listings annotated with samples
dot Outputs a graph in DOT format
eog Visualize graph through eog
evince Visualize graph through evince
gif Outputs a graph image in GIF format
gv Visualize graph through gv
kcachegrind Visualize report in KCachegrind
list Output annotated source for functions matching regexp
pdf Outputs a graph in PDF format
peek Output callers/callees of functions matching regexp
png Outputs a graph image in PNG format
proto Outputs the profile in compressed protobuf format
ps Outputs a graph in PS format
raw Outputs a text representation of the raw profile
svg Outputs a graph in SVG format
tags Outputs all tags in the profile
text Outputs top entries in text form
top Outputs top entries in text form
topproto Outputs top entries in compressed protobuf format
traces Outputs all profile samples in text form
tree Outputs a text rendering of call graph
web Visualize graph through web browser
weblist Display annotated source in a web browser
o/options List options and their current values
quit/exit/^D Exit pprof
...
//使用top 5查看前5个耗cpu的调用
(pprof) top 5
Showing nodes accounting for 5.99s, 99.50% of 6.02s total
Dropped 5 nodes (cum <= 0.03s)
flat flat% sum% cum cum%
5.94s 98.67% 98.67% 5.99s 99.50% command-line-arguments.Fibonacci
0.05s 0.83% 99.50% 0.05s 0.83% runtime.newstack
0 0% 99.50% 5.99s 99.50% command-line-arguments.BenchmarkFibonacci
0 0% 99.50% 5.99s 99.50% testing.(*B).launch
0 0% 99.50% 5.99s 99.50% testing.(*B).runN
...
- web查看
//先安装[graphviz](http://www.graphviz.org/download/)
//加上--web
go tool pprof --web base.test profile_cpu.out
//此方式会生成一个.svg格式的文件,可以用任何支持.svg的软件打开
- pdf输出
//加上-pdf选项,并把数据重定向到新的pdf格式文件即可
go tool pprof base.test -pdf profile_cpu.out > profile_cpu.pdf
3. 服务版的pprof性能分析
要使用服务版的pprof,只需在启动服务的main中引入相关包,启动服务即可。
"net/http"
_ "net/http/pprof"
运行服务时,你的 HTTP 服务会多出 /debug/pprof 这个访问路径,用于查看服务器版的性能分析报告,例如:访问http://{hostname}:{port}/debug/pprof/
可以通过访问各自类型的性能分析页面了解服务的总体情况:
- cpu: http://{hostname}:{port}/debug/pprof/profile,默认进行 30s 的 CPU Profiling,得到一个分析用的 profile 文件
- block:http://{hostname}:{port}/debug/pprof/block,查看导致阻塞同步的堆栈跟踪
- goroutine:http://{hostname}:{port}/debug/pprof/goroutine,查看当前所有运行的 goroutines 堆栈跟踪
- heap: http://{hostname}:{port}/debug/pprof/heap,查看活动对象的内存分配情况
- mutex:http://{hostname}:{port}/debug/pprof/mutex,查看导致互斥锁的竞争持有者的堆栈跟踪
- threadcreate:http://{hostname}:{port}/debug/pprof/threadcreate,查看创建新OS线程的堆栈跟踪