单元测试和性能测试
参考:
常用的测试参数
-test.v : 是否输出全部的单元测试用例(不管成功或者失败),默认没有加上,所以只输出失败的单元测试用例。
-test.run pattern: 只跑哪些单元测试用例
-test.bench patten: 只跑那些性能测试用例
-test.benchmem : 是否在性能测试的时候输出内存情况
-test.benchtime t : 性能测试运行的时间,默认是1s
-test.outputdir dir : 性能分析文件存放的目录
-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 : 程序运行在哪些CPU上面,使用二进制的1所在位代表,和nginx的nginx_worker_cpu_affinity是一个道理
-test.short : 将那些运行时间较长的测试用例运行时间缩短
==注意:文件必须是_test结尾==
代码
func C1(s []string) string {
return strings.Join(s," ") //连接字符串切片
}
单元测试
书写测试代码
import "testing"
func TestC1(t *testing.T) {
if C1([]string{"hello","word"}) != "hello word" {
t.Error("测试没通过")
}
}
执行测试
go test -v .
go test --cover . #单元测试覆盖率
性能测试
测试代码(测试函数C1)
func BenchmarkC1(b *testing.B) {
b.ReportAllocs()//在report中包含内存分配信息
for i:=0;i < b.N; i++ {
C1([]string{"hello","word"})
}
}
执行测试
go test -bench=".*" -benchtime=5s -count=3 -cpuprofile=cpu.out ./
命令参数解释
-bench: 执行压力测试的匹配到的文件
-benchtime: 默认时间是1s,除了是时间外,还可以是具体的次数。例如,执行30次可以用 `-benchtime=30x`
-count: 参数可以用来设置 benchmark 的轮数
-cpuprofile: 输出cpu性能分析文件,并命名cpu.out
./ :输出文件到当前路径
## 执行前文件目录
test
├── s.go
└── s_test.go
## 执行后文件目录
test
├── cpu.out //cpu性能文件
├── s.go
├── s_test.go
└── test.test //测试输出文件
测试输出
goos: linux //系统
goarch: amd64 //平台
BenchmarkC1-8 //8为多少cpu参与执行 20000000 //执行次数 80.1 ns/op //每次执行的平均时间 16 B/op // 每次分配的内存 1 allocs/op //每次分配的对象
PASS //通过,失败为FAIL
ok _/home/meng/Project/go/test/src/test 1.807s //总耗时
pprof的使用
package main
import (
"fmt"
"net/http"
_ "net/http/pprof" //这个包可以帮助分析cpu mem thread等
)
func hello(w http.ResponseWriter,r *http.Request) {
w.Write([]byte("hello word!"))
}
func main() {
// pprof 的init函数会将pprof里的一些handler注册到http.DefaultServeMux上
// 当不使用http.DefaultServeMux来提供http api时,可以查阅其init函数,自己注册handler
//go func() {
// http.HandleFunc("/debug/pprof/block", pprof.Index)
// http.HandleFunc("/debug/pprof/goroutine", pprof.Index)
// http.HandleFunc("/debug/pprof/heap", pprof.Index)
// http.HandleFunc("/debug/pprof/threadcreate", pprof.Index)
//
// http.ListenAndServe("0.0.0.0:8888", nil)
//}()
http.HandleFunc("/hello",hello)
if err := http.ListenAndServe(":8888",nil);err != nil {
fmt.Println(err.Error())
}
}
你可以通过下面的命令采集30s的数据并生成SVG调用图:
go tool pprof -web http://127.0.0.1:8888/debug/pprof/profile
查看复杂函数内部的耗时进行优化
go tool pprof test.test cpu.out
输入top10查看
File: test.test
Type: cpu
Time: Jul 8, 2018 at 1:49pm (CST)
Duration: 1.80s, Total samples = 1.73s (95.95%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top10
Showing nodes accounting for 1610ms, 93.06% of 1730ms total
Showing top 10 nodes out of 51
flat flat% sum% cum cum%
480ms 27.75% 27.75% 1350ms 78.03% runtime.concatstrings
470ms 27.17% 54.91% 560ms 32.37% runtime.mallocgc
150ms 8.67% 63.58% 150ms 8.67% runtime.memmove
120ms 6.94% 70.52% 1560ms 90.17% strings.Join
90ms 5.20% 75.72% 1440ms 83.24% runtime.concatstring3
90ms 5.20% 80.92% 720ms 41.62% runtime.rawstringtmp
70ms 4.05% 84.97% 630ms 36.42% runtime.rawstring
60ms 3.47% 88.44% 1700ms 98.27% _/home/meng/Project/go/test/src/test.BenchmarkC1
60ms 3.47% 91.91% 1620ms 93.64% _/home/meng/Project/go/test/src/test.C1
20ms 1.16% 93.06% 20ms 1.16% runtime.duffcopy
(pprof) web //生成svg图片,并打开浏览器查看
ATTENTION: default value of option force_s3tc_enable overridden by environment.
==此步骤需要安装FlameGraph==
go get github.com/uber/go-torch
cd $GOPATH/src/github.com/uber/go-torch
git clone https://github.com/brendangregg/FlameGraph.git #安装火焰图插件
如果调用链路太复杂,可以查看火焰图
使用uber/go-torch这个库
go-torch -b cpu.out //使用cpuprofile文件生成火焰图
file:///home/meng/Project/go/test/src/test/torch.svg //浏览器查看
优化
减少内存分配等
测试目录下的所有文件
go test -bench=".*" ./*/
// ./*/ 代表当前目录下的所有目录的文件
简单的http测试
handle
package main
import (
"fmt"
"log"
"net/http"
)
func Test(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "test hello")
}
func main() {
http.HandleFunc("/test", Test)
log.Println("Listening...")
http.ListenAndServe(":3000", nil)
}
测试代码
package main
import (
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)
func TestTest(t *testing.T) {
//测试方法1:
//创建请求
req, err := http.NewRequest("GET", "/r", nil)
assert.NoError(t, err)
//记录响应
rr := httptest.NewRecorder()
Test(rr, req)
//检查返回
assert.Equal(t, http.StatusOK, rr.Code)
//返回的数据
assert.Equal(t, "test hello\n", rr.Body.String())
//测试方法2:
//assert
assert.HTTPSuccess(t, Test, "GET", "", nil)
assert.HTTPBodyContains(t, Test, "GET", "", nil, "hello")
}
端到端测试
golang分层测试之httpexpect+httptest测试应用 httpexpect 端到端的测试框架
输出测试覆盖率
shell script
go test -race -cover -coverprofile=./coverage.out -timeout=10m -short -v ./...
go tool cover -func ./coverage.out