learning_notes

学习笔记

View project on GitHub

单元测试和性能测试

参考:

性能分析

常用的测试参数

-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