Overview
在一个软件的生命周期中,测试test是非常重要的一环。
关系到代码质量,稳定性,可维护性。可以让你在代码重构时及时发现错误,以便更加大胆地去尝试重构。
全流程
目前我的整个go测试包括主要有,
- 编写生产代码
-
自动生成测试代码(根据生产代码)
- mock对象,如果测试代码的client很难在线获取到数据,使用mock对象替代之(if necessary)
- 运行测试代码
go test -v
- 观察测试指标
- accuracy
- coverage
- benchmark(performance)
编写生产代码
定义一个struct,用它来实现data的更新(数据来源于aws s3 obj)和获取value(根据key)
type Storage struct {
s3Client s3.ClientAPI // 这里是interface类型
mu sync.RWMutex
data map[string]string
}
func (p *Storage) Get(key string) (string, error) {}
func (p *Storage) Set(obj string) error {}
测试代码
自动生成部分
这里使用gotests[1]来自动生成表格驱动测试代码
func TestStorage_Get(t *testing.T) {}
func TestStorage_Set(t *testing.T) {}
这里有2点需要注意,
-
自动生成脚本可以内嵌到go文件中
-
因为这里是从s3中获取源数据,在local环境下一般是没有这样的accessKey,或者说一般也不会在测试时直连s3,这里s3类似MySQL, es,因此在此我们可以把s3的返回mock掉
- 这里使用mockery[2]来自动对s3 client的接口进行mock
- 这里mock接口(ClientAPI interface),而不是mock结构体(Client struct),因为Client实现了ClientAPI的全部方法,就是说Client是ClientAPI的实现者[3],Client < ClientAPI
- 例如,这里只需要一个接口(CRUD),但是可以有多个结构体(s3Client, mysqlClient, esClient),这些struct都实现了该接口,那么这个mock就可以在各个struct里被正常调用
- 这里使用mockery[2]来自动对s3 client的接口进行mock
手动生成部分
因为benchmark文件我还没找到可以自动生成的工具,所以采用了手动的方式,下面是一些格式要求,
- 代码文件必须以
_test.go
结尾 - 函数必须以
Benchmark
开头
func BenchmarkGet(b *testing.B) {}
func BenchmarkSet(b *testing.B) {}
运行测试代码
运行UT
go test ./... -v -race -count=1 -cover
运行Benchmark
go test -v -bench=. -run=none -cpuprofile cpu.prof -memprofile mem.prof -count=1 -cover
观察测试指标
运行测试代码后会产生一系列的监控指标,比如-v
展示了各个func的运行时间,覆盖率,cpu和mem消耗等
在运行完测试代码之后,如果对测试结果满意,就可以commit并提PR了
UT指标
➜ golang-auto-test-example git:(master) go test ./... -v -race -count=1 -cover
? golang-auto-test-example [no test files]
? golang-auto-test-example/api [no test files]
? golang-auto-test-example/api/mocks [no test files]
=== RUN TestStorage_Bootstrap
=== RUN TestStorage_Bootstrap/Success
time="2019-09-05T17:34:30+08:00" level=info msg="<nil>"
time="2019-09-05T17:34:30+08:00" level=info msg="&{0 0xc00010c0b8}"
--- PASS: TestStorage_Bootstrap (0.00s)
--- PASS: TestStorage_Bootstrap/Success (0.00s)
=== RUN TestStorage_Get
=== RUN TestStorage_Get/Success
=== RUN TestStorage_Get/Failure
--- PASS: TestStorage_Get (0.00s)
--- PASS: TestStorage_Get/Success (0.00s)
--- PASS: TestStorage_Get/Failure (0.00s)
=== RUN TestStorage_Set
=== RUN TestStorage_Set/Success
=== RUN TestStorage_Set/Failure
--- PASS: TestStorage_Set (0.00s)
--- PASS: TestStorage_Set/Success (0.00s)
--- PASS: TestStorage_Set/Failure (0.00s)
prd_test.go:160: PASS: GetObject(string)
prd_test.go:160: PASS: GetObject(string)
PASS
coverage: 95.2% of statements
ok golang-auto-test-example/prd 1.124s coverage: 95.2% of statements
➜ golang-auto-test-example git:(master)
Benchmark指标
➜ golang-auto-test-example git:(master) cd prd
➜ prd git:(master) go test -v -bench=. -run=none -cpuprofile cpu.prof -memprofile mem.prof -count=1 -cover
goos: darwin
goarch: amd64
pkg: golang-auto-test-example/prd
BenchmarkGet-12 10000000 199 ns/op
BenchmarkSet-12 100000 13352 ns/op
PASS
coverage: 71.4% of statements
ok golang-auto-test-example/prd 3.854s
➜ prd git:(master)
➜ prd git:(master) cd -
your/path/to/golang-auto-test-example
➜ golang-auto-test-example git:(master)
运行了上述命令之后,会生成2个profile文件,这些文件可以通过pprof[4]来查看
其中pprof已经集成在go tool
里面,只需要安装Graphviz
即可用于DOT看图
brew list graphviz &>/dev/null || brew install graphviz
# enter web interactive mode
go tool pprof -http=:8080 prd/cpu.prof
go tool pprof -http=:8081 prd/mem.prof
Graph
- 在Graph这一栏可以查看CPU的耗时/结构体的内存消耗
- 方块越大(线越粗)代表它的消耗越大(Caution),方块颜色无特殊意义
Flame Graph,火焰图
- 在Flame Graph这一栏可以查看CPU的耗时/结构体的内存消耗
- 方块宽度越大代表它的消耗越大(Caution),方块高度和颜色无特殊意义
- 方块的叠加层数代表调用栈深度(root在top)
- 调用栈在横向是按照字母排序,而同一个调用栈会自动合并,所以一个方块越宽其越有可能成为瓶颈[7]
命令
最后汇总一下auto test
的命令,这些命令可以在copy到Terminal里面运行(注意目录所在),也可以在go文件里面点击绿色三角标直接运行,各个命令的功能如下,
Reference
PREVIOUSprotobuf关注点
NEXTscala自动化测试关注点