Go语言程序测试

xcltapestry · · 9226 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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

这篇主要讲这几点:

一.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之类怎么测试及性能优化方面的再另写.


BLOG: http://blog.csdn.ent/xcl168




有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:xcltapestry

查看原文:Go语言程序测试

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

9226 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传