0
点赞
收藏
分享

微信扫一扫

Go语言程序测试


   最近一直在搞各种测试,然后今天周末翻翻书,发现特价萝卜的书上在测试方面写得挺全的。 
这书是之前 ​CU​论坛。搞活动得到奖品(还有作者亲笔签名),拿回来都没看完.
因为写得太太太细了,又厚。
    参考他的书及官网的文档,再把测试方面的东西过一下还是有点意思的.

这篇主要讲这几点:

一.Testing的几种不同形式
功能测试:
TestXxxx(t *testing.T) 

基准测试:
BenchmarkXxxx(b *testing.B)

样本测试:
Example_Xxx() 

用于测试的Main函数:

TestMain(m *testing.M)

//

//
func TestMain(m *testing.M) {

//
flag.Parse()

//
os.Exit(m.Run())

//
}



二. 测试中的资源收集

三. 测试覆盖率


一.Testing的几种不同形式
功能测试:

TestXxxx(t *testing.T) 
   偏向于测试某个源码文件中的具体源码功能实现。
   基本是在同一个包位置。
   go test 来运行.
   //运行包下的所有测试
   go test -v prj/pkg
   //只测试这个包下名称与之匹配的功能测试函数
   go test -v -run=xxfunc prj/pkg


控制测试运行时间:
 -timeout : 超时控制
   go test -timeout 100ms prj/pkg  //可以用1h20s之类表示时间
   (时间单位: h小时/m分钟/s秒/ms毫秒/us微秒/ns纳秒)   
  
 -short : 是否缩短测试时间   
   可以在测试代码中加上这个if
   if testing.Short() {
   }else{
   }
   然后在运行命令中加上 -short参数,如:
   go test -short prj/pkg

并发控制:
  测试函数默认是不并发执行的.如要并发执行.除了设置GOMAXPROCS外,
   runtime.GOMAXPROCS( runtime.NumCPU())
   还要在要测试的函数中开始,加上

t.Parallel()   

func TestParallel(t *testing.T){
t.Parallel()
//......
}
go test -parallel 8

基准测试:

BenchmarkXxxx(b *testing.B)

 运行:    
  go test benxxx_test.go -bench="." 
  go test -bench=xxfunc
  go test -bench=xxfunc prj/pkg

 Go的Benchmark默认是在1s的时间内,尽可能多的执行这个函数. 可以通过 -benchtime 这个参数扩大时长.
 go test benxxx_test.go -benchtime=2s

 标准包中的相关源码:

var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
// Run the benchmark for at least the specified amount of time.
d := *benchTime
for !b.failed && b.duration < d && n < 1e9 {


测试函数模板例子:  

func BenchmarkXxxx(b *testing.B){
var (
//初始化
)

b.StopTimer() //停止测试计时,因为默认是打开计时的.

// 一些不需要计入测试时间的动作
// ......

b.StartTimer() //开始测试计时


// 继续运行测试
// 如:
for i:=0;i<b.N;i++{
//......
}


// 如有重置计数时间需求
// RestTimer() 把之前累计的函数执行时间重置成0
// 继续运行测试
// ........
}


Benchmark 与功能测试不同的地方在于,它更多的关注性能之类的指标上.


例子:


package main

import (
"fmt"
"testing"
)

func BenchmarkXxxx(b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Println("i:", i)
}
}

测试结果:


   30000             50806 ns/op


ok      _/E_/GOtest/testing/bentest   2.179s



30000 :总运行次数


50806 ns/op : 平均运行时间,即平均运行一次,要50806纳秒



另外还可以加 t.SetBytes(1111111)得到每次能向文件系统写入多mb数据

/*
Benchmark 例子
Author:xcl
Date:2015-11-22
*/
package main

import (
"fmt"
"math/big"
"testing"
)

func BenchmarkXxxx(b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Sprintf("hello %d", i)
}
}

func BenchmarkBigLen(b *testing.B) {
big := NewBig()
b.ResetTimer()
for i := 0; i < b.N; i++ {
big.BitLen()
}
}

func NewBig() *big.Int {
x := new(big.Int)
x.MulRange(1, 10)
return x
}

/*
//测试结果:

E:\GOtest\testing\bentest>go test b_test.go -bench=. -benchmem -cpu=1,2,4,8
testing: warning: no tests to run
PASS
BenchmarkXxxx 5000000 341 ns/op 32 B/op 2 allocs/op
BenchmarkXxxx-2 5000000 323 ns/op 32 B/op 2 allocs/op
BenchmarkXxxx-4 5000000 332 ns/op 32 B/op 2 allocs/op
BenchmarkXxxx-8 5000000 330 ns/op 32 B/op 2 allocs/op
BenchmarkBigLen 200000000 9.85 ns/op 0 B/op 0 allocs/op
BenchmarkBigLen-2 200000000 9.86 ns/op 0 B/op 0 allocs/op
BenchmarkBigLen-4 100000000 10.0 ns/op 0 B/op 0 allocs/op
BenchmarkBigLen-8 200000000 9.82 ns/op 0 B/op 0 allocs/op
ok command-line-arguments 18.085s


*/


样本测试:

Example_Xxx() 


  用来看运行中,输出的内容是否与预期的一样


   func ExampleXxxx() 


 例子:  

func ExampleHello() {

fmt.Println("hello")

// Output: hello

}


就是在下面,加一行 "// Output: 预期结果" 用来对比结果.


它的样本函数命令有些规则:


   被测试对象是函数时:


func Example() { ... } //被测试对象是整个包

func ExampleF() { ... } //被测试对象是函数

func ExampleT() { ... } //被测试对象是类型

func ExampleT_M() { ... } //被测试对象是某个类型的一个函数



依前面规则取名后,还可以在后面加后缀以例区分


func Example_suffix() { ... }

func ExampleF_suffix() { ... }

func ExampleT_suffix() { ... }

func ExampleT_M_suffix() { ... }


二. 测试中的资源收集

 在测试运行时,可以加上一些参数,用来采集监控资源使用情况。然后依Go提供的工具,作分析.

  -cpuprofile cpu.out // 默认每10毫秒采样一次,cpu的使用情况 
  -memprofile mem.out  //程序运行期间,堆内存的分配情况
  -memprofilerate n     //内存分配行为,默认每分配512k字节,采样一次
  -blockprofile block.out  //记录Goroutine阻塞事件
  -blockprofilerate n  // 控制记录Goroutine阻塞时候打点的纳秒数。默认不设置
    // 就相当于-test.blockprofilerate=1,每一纳秒都打点记录一下

以cpuprofile为例:

E:\GOtest\testing\bentest>go test b_test.go -bench=. -benchmem -cpu=1,2,4,8  -cpuprofile cpu.out
testing: warning: no tests to run
PASS
BenchmarkXxxx 5000000 351 ns/op 32 B/op 2 allocs/op
BenchmarkXxxx-2 5000000 326 ns/op 32 B/op 2 allocs/op
BenchmarkXxxx-4 5000000 326 ns/op 32 B/op 2 allocs/op
BenchmarkXxxx-8 5000000 332 ns/op 32 B/op 2 allocs/op
BenchmarkBigLen 200000000 9.91 ns/op 0 B/op 0 allocs/op
BenchmarkBigLen-2 200000000 9.84 ns/op 0 B/op 0 allocs/op
BenchmarkBigLen-4 100000000 10.2 ns/op 0 B/op 0 allocs/op
BenchmarkBigLen-8 100000000 10.2 ns/op 0 B/op 0 allocs/op
ok command-line-arguments 16.231s

E:\GOtest\testing\bentest>dir
驱动器 E 中的卷是 doc
卷的序列号是 0E3D-2A1F

E:\GOtest\testing\bentest 的目录

2015/11/22 11:59 <DIR> .
2015/11/22 11:59 <DIR> ..
2015/11/22 11:44 1,423 b_test.go
2015/11/22 11:59 63,024 cpu.out
2015/11/22 11:59 3,932,160 main.test.exe
3 个文件 3,996,607 字节
2 个目录 15,239,778,304 可用字节

        注意,生成了一个叫"main.test.exe"的可执行文件及cpu.out的输出文件。


可以利用它们来做分析


     go tool pprof main.test.exe cpu.out


//查阅运行中,CPU使用情况
E:\GOtest\testing\bentest>go tool pprof main.test.exe cpu.out
Entering interactive mode (type "help" for commands)
(pprof) top10
12230ms of 16500ms total (74.12%)
Dropped 62 nodes (cum <= 82.50ms)
Showing top 10 nodes out of 55 (cum >= 1890ms)
flat flat% sum% cum cum%
2910ms 17.64% 17.64% 4330ms 26.24% math/big.nat.bitLen
2850ms 17.27% 34.91% 7180ms 43.52% math/big.(*Int).BitLen
1420ms 8.61% 43.52% 1420ms 8.61% math/big.bitLen
1090ms 6.61% 50.12% 1250ms 7.58% runtime.mallocgc
1020ms 6.18% 56.30% 3510ms 21.27% fmt.(*pp).doPrintf
840ms 5.09% 61.39% 8020ms 48.61% command-line-arguments.BenchmarkBigLen
820ms 4.97% 66.36% 1090ms 6.61% fmt.(*fmt).integer
550ms 3.33% 69.70% 550ms 3.33% runtime.memmove
400ms 2.42% 72.12% 400ms 2.42% runtime.mSpan_Sweep.func1
330ms 2.00% 74.12% 1890ms 11.45% fmt.(*pp).printArg
(pprof)

(pprof) help

Commands:
cmd [n] [--cum] [focus_regex]* [-ignore_regex]*
Produce a text report with the top n entries.
Include samples matching focus_regex, and exclude ignore_regex.
Add --cum to sort using cumulative data.
Available commands:
callgrind Outputs a graph in callgrind format
disasm Output annotated assembly for functions matching regexp or address
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
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
tree Outputs a text rendering of call graph
web Visualize graph through web browser
weblist Output annotated source in HTML for functions matching regexp or address
peek func_regex
Display callers and callees of functions matching func_regex.
.........

(pprof) weblist
(pprof) web
Cannot find dot, have you installed Graphviz?

下载安装个 Graphviz ,就能出图了.



用于测试的Main函数:
TestMain(m *testing.M)

  来个例子:   

/*
TestMain例子

Author:xcl
Date: 2015-11-22
*/
package main

import (
"flag"
"log"
"os"
"testing"
)

var wordPtr = flag.String("word", "foo", "a string")

func TestMain(m *testing.M) {

flag.Parse()
log.Println("[TestMain] word:", *wordPtr)

log.Println("[TestMain] run()前")
exitVal := m.Run()
log.Println("[TestMain] run()后")

os.Exit(exitVal)
}

func Test1(t *testing.T) {
log.Println("[Test1] running ", *wordPtr)
}

/*

E:\GOtest\testing\t2>go test t2_test.go -v -trace trace.out -word xcl......
2015/11/22 18:11:43 [TestMain] word: xcl......
2015/11/22 18:11:43 [TestMain] run()前
=== RUN Test1
2015/11/22 18:11:43 [Test1] running xcl......
--- PASS: Test1 (0.00s)
PASS
2015/11/22 18:11:43 [TestMain] run()后
ok command-line-arguments 0.101s

E:\GOtest\testing\t2>dir
驱动器 E 中的卷是 doc
卷的序列号是 0E3D-2A1F

E:\GOtest\testing\t2 的目录

2015/11/22 18:11 <DIR> .
2015/11/22 18:11 <DIR> ..
2015/11/22 18:11 3,666,944 main.test.exe
2015/11/22 18:11 2,234 t2_test.go
2015/11/22 18:11 730 trace.out
3 个文件 3,669,908 字节
2 个目录 15,177,601,024 可用字节

E:\GOtest\testing\t2>go tool trace main.test.exe trace.out

*/


三.测试覆盖率:

   覆盖率是指被测试对象有多少代码在刚刚的测试中被用到。
   go test -cover prj/pkg
   go test prj/pkg -coverpkg=pkg1,pkg2
   go test prj/pkg -coverpkg=pkg1,pkg2 -coverprofile=cover.out
   go tool cover -html=cover.out

-func=cover.out  //输出每个函数的测试覆盖率概要信息
-html=cover.out //将概要文件内容转成html并浏览
-mode=cover.out //设计概要文件编译模式
-o=cover.out //...
-var=GoCover  // ...


参考资料: ​​ https://golang.org/pkg/testing/​​

 ​​https://golang.org/cmd/go/#hdr-Description_of_testing_flags​​​​ https://github.com/polaris1119/The-Golang-Standard-Library-by-Example/blob/master/chapter09/09.1.md​​          郝林的<<Go语言并发编程实战>>测试篇

      

其实细节,具体查相关资料。 另外,Go源码包下也有很多详细的例子.

先整理到这,HTTP/WebSocket之类怎么测试及性能优化方面的再另写.




举报

相关推荐

0 条评论