随笔-生活工作点滴Go基础系列

11 Go测试

2019-07-06  本文已影响3人  GoFuncChan

一、测试基础概述

Go语言内建一套基于表格驱动测试的机制,只需编写少量代码,就可对特定功能进行单元测试和基准测试(性能测试或压力测试)

二、单元测试

所谓单元测试,是程序中对最小功能单元进行检查和验证。

Go进行单元测试非常简单:

func TestAXXX( t *testing.T ){}

func TestBXXX( t *testing.T ){}
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进行基准测试和单元测试一样简单:

func Benchmark_AXXX( t *testing.B ){}

func Benchmark_BXXX( t *testing.B ){}
-bench=.表示运行 benchmark_test.go 文件里的所有基准测试,和单元测试中的-run类似

基准测试用例:

func BenchmarkFibonacci(b *testing.B) {
    b.Log("开始压力测试斐波那契数列!!!!")
    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
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
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
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
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
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
    TestBenchmark_test.go:25: 开始压力测试斐波那契数列!!!!
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包来做代码的性能分析

1. pprof是什么?

pprof是Go提供的可视化性能分析工具,在性能测试中读取分析样本的集合,并生成报告以可视化并帮助分析数据。pprof既能生成报告文件,也可以借助graphviz生成web界面。

你能看到什么?

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

...
//先安装[graphviz](http://www.graphviz.org/download/)
//加上--web
go tool pprof --web base.test profile_cpu.out

//此方式会生成一个.svg格式的文件,可以用任何支持.svg的软件打开
//加上-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/

可以通过访问各自类型的性能分析页面了解服务的总体情况:
上一篇下一篇

猜你喜欢

热点阅读