golang自动化测试关注点

 

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点需要注意,

  1. 自动生成脚本可以内嵌到go文件中

    gotests auto generate

  2. 因为这里是从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里被正常调用

    mock  auto generate

手动生成部分

因为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了 :grin:

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

image

image

  • 在Graph这一栏可以查看CPU的耗时/结构体的内存消耗
  • 方块越大(线越粗)代表它的消耗越大(Caution),方块颜色无特殊意义

Flame Graph,火焰图

image

image

  • 在Flame Graph这一栏可以查看CPU的耗时/结构体的内存消耗
  • 方块宽度越大代表它的消耗越大(Caution),方块高度和颜色无特殊意义
  • 方块的叠加层数代表调用栈深度(root在top)
  • 调用栈在横向是按照字母排序,而同一个调用栈会自动合并,所以一个方块越宽其越有可能成为瓶颈[7]

命令

最后汇总一下auto test的命令,这些命令可以在copy到Terminal里面运行(注意目录所在),也可以在go文件里面点击绿色三角标直接运行,各个命令的功能如下, image

Reference

  1. gotests
  2. mockery
  3. 理解Go interface的5个关键点
  4. pprof
  5. go benchmark实践与原理
  6. golang 单元测试(gotests、mockery自动生成)
  7. 白话火焰图
  8. my source code